diff --git a/package.json b/package.json index 3271efa4..72e7f7c1 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "jest-mock": "^24.9.0", "jest-environment-node": "^24.9.0", "extract-zip": "^1.6.7", + "node-stream-zip": "^1.8.2", "xmldom-alpha": "^0.1.28", "xpath": "0.0.24", "escape-html": "^1.0.3", diff --git a/packages/ace-axe-runner-electron/src/init.js b/packages/ace-axe-runner-electron/src/init.js index d06e0974..f94b596a 100644 --- a/packages/ace-axe-runner-electron/src/init.js +++ b/packages/ace-axe-runner-electron/src/init.js @@ -22,6 +22,8 @@ const generateSelfSignedData = require('./selfsigned').generateSelfSignedData; const isDev = process && process.env && (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true'); const showWindow = false; +const LOG_DEBUG_URLS = process.env.LOG_DEBUG_URLS === "1"; + const LOG_DEBUG = false; const ACE_LOG_PREFIX = "[ACE-AXE]"; @@ -61,7 +63,12 @@ function loadUrl(browserWindow) { browserWindow.ace__timeout = undefined; const options = {}; // { extraHeaders: 'pragma: no-cache\n' }; - browserWindow.loadURL(`${rootUrl}${browserWindow.ace__currentUrl}?${HTTP_QUERY_PARAM}=${iHttpReq++}`, options); + const uareel = `${rootUrl}${browserWindow.ace__currentUrl}?${HTTP_QUERY_PARAM}=${iHttpReq++}`; + if (LOG_DEBUG_URLS) { + console.log("======>>>>>> URL TO LOAD"); + console.log(uareel); + } + browserWindow.loadURL(uareel, options); const MILLISECONDS_TIMEOUT_INITIAL = 5000; // 5s max to load the window's web contents const MILLISECONDS_TIMEOUT_EXTENSION = 35000; // 40s max to load + execute Axe checkers @@ -339,12 +346,37 @@ function axeRunnerInit(eventEmmitter, CONCURRENT_INSTANCES) { const scripts = payload.scripts; const scriptContents = payload.scriptContents; + if (LOG_DEBUG_URLS) { + console.log("######## URL 1"); + console.log(uarel); + } // windows! file://C:\aa\bb\chapter.xhtml const uarelObj = url.parse(uarel.replace(/\\/g, "/")); const windowsDrive = uarelObj.hostname ? `${uarelObj.hostname.toUpperCase()}:` : ""; + if (LOG_DEBUG_URLS) { + console.log("######## URL 2"); + console.log(windowsDrive); + } const bd = basedir.replace(/\\/g, "/"); + if (LOG_DEBUG_URLS) { + console.log("######## URL 3"); + console.log(uarelObj.pathname); + } const full = (windowsDrive + decodeURI(uarelObj.pathname)); - const httpUrl = full.replace(bd, ""); + if (LOG_DEBUG_URLS) { + console.log("######## URL 4"); + console.log(full); + } + let httpUrl = full.replace(bd, ""); + if (LOG_DEBUG_URLS) { + console.log("######## URL 5"); + console.log(httpUrl); + } + httpUrl = encodeURI(httpUrl); + if (LOG_DEBUG_URLS) { + console.log("######## URL 6"); + console.log(httpUrl); + } if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} axeRunner running ... ${basedir} --- ${uarel} => ${httpUrl}`); @@ -553,7 +585,20 @@ function startAxeServer(basedir, scripts, scriptContents) { if (req.query[HTTP_QUERY_PARAM]) { if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} HTTP intercept ${req.url}`); - const pn = url.parse(req.url).pathname; + if (LOG_DEBUG_URLS) { + console.log(">>>>>>>>>> URL 1"); + console.log(req.url); + } + const ptn = url.parse(req.url).pathname; + if (LOG_DEBUG_URLS) { + console.log(">>>>>>>>>> URL 2"); + console.log(ptn); + } + const pn = decodeURI(ptn); + if (LOG_DEBUG_URLS) { + console.log(">>>>>>>>>> URL 3"); + console.log(pn); + } let fileSystemPath = path.join(expressApp.basedir, pn); if (LOG_DEBUG) console.log(`${ACE_LOG_PREFIX} filepath to read: ${fileSystemPath}`); if (!fs.existsSync(fileSystemPath)) { @@ -585,7 +630,7 @@ function startAxeServer(basedir, scripts, scriptContents) { expressApp.use("/", (req, res, next) => { // const url = new URL(`https://fake.org${req.url}`); // const pathname = url.pathname; - const pathname = url.parse(req.url).pathname; + const pathname = decodeURI(url.parse(req.url).pathname); const filePath = path.join(basedir, pathname); if (filePathsExpressStaticNotExist[filePath]) { diff --git a/packages/ace-core/src/checker/checker-chromium.js b/packages/ace-core/src/checker/checker-chromium.js index 6a7221be..8af3f028 100644 --- a/packages/ace-core/src/checker/checker-chromium.js +++ b/packages/ace-core/src/checker/checker-chromium.js @@ -24,9 +24,17 @@ const scripts = [ require.resolve('../scripts/ace-extraction.js'), ]; +const LOG_DEBUG_URLS = process.env.LOG_DEBUG_URLS === "1"; + async function checkSingle(spineItem, epub, lang, axeRunner) { winston.verbose(`- Processing ${spineItem.relpath}`); try { + if (LOG_DEBUG_URLS) { + console.log("....... URL 1"); + console.log(spineItem.url); + console.log(spineItem.filepath); + console.log(spineItem.relpath); + } let url = spineItem.url; let ext = path.extname(spineItem.filepath); @@ -39,8 +47,17 @@ async function checkSingle(spineItem, epub, lang, axeRunner) { const tmpdir = tmp.dirSync({ unsafeCleanup: true }).name; const tmpFile = path.join(tmpdir, `${path.basename(spineItem.filepath, ext)}.xhtml`) fs.copySync(spineItem.filepath, tmpFile); + + // does encodeURI() as per https://tools.ietf.org/html/rfc3986#section-3.3 in a nutshell: encodeURI(`file://${tmpFile}`).replace(/[?#]/g, encodeURIComponent) url = fileUrl(tmpFile); - winston.debug(`checking copied file at ${url}`) + // url = "file://" + encodeURI(tmpFile); + + winston.debug(`checking copied file at ${tmpFile}`) + } + + if (LOG_DEBUG_URLS) { + console.log("....... URL 2"); + console.log(url); } const scriptContents = []; @@ -105,18 +122,50 @@ async function checkSingle(spineItem, epub, lang, axeRunner) { if (Array.isArray(item.src)) { item.src = item.src.map((srcItem) => { if (srcItem.src !== undefined) { - srcItem.path = path.resolve(path.dirname(spineItem.filepath), - srcItem.src.toString()); + if (LOG_DEBUG_URLS) { + console.log("----- ITEMs SRC 1"); + console.log(srcItem.src); + } + srcItem.path = path.resolve(path.dirname(spineItem.filepath), decodeURI(srcItem.src.toString())); + if (LOG_DEBUG_URLS) { + console.log("----- ITEMs SRC 2"); + console.log(srcItem.path); + } srcItem.src = path.relative(epub.basedir, srcItem.path).replace(/\\/g, "/"); + if (LOG_DEBUG_URLS) { + console.log("----- ITEMs SRC 3"); + console.log(srcItem.src); + } } return srcItem; }); } else { - item.path = path.resolve(path.dirname(spineItem.filepath), item.src.toString()); + if (LOG_DEBUG_URLS) { + console.log("----- ITEM SRC 1"); + console.log(item.src); + } + item.path = path.resolve(path.dirname(spineItem.filepath), decodeURI(item.src.toString())); + if (LOG_DEBUG_URLS) { + console.log("----- ITEM SRC 2"); + console.log(item.path); + } item.src = path.relative(epub.basedir, item.path).replace(/\\/g, "/"); + if (LOG_DEBUG_URLS) { + console.log("----- ITEM SRC 3"); + console.log(item.src); + } } if (item.cfi !== undefined) { - item.location = `${spineItem.relpath}#epubcfi(${item.cfi})`; + if (LOG_DEBUG_URLS) { + console.log("----- CFI 1"); + console.log(spineItem.relpath); + console.log(item.cfi); + } + item.location = `${encodeURI(spineItem.relpath)}#epubcfi(${encodeURI(item.cfi)})`; + if (LOG_DEBUG_URLS) { + console.log("----- CFI 2"); + console.log(item.location); + } delete item.cfi; } } @@ -125,6 +174,7 @@ async function checkSingle(spineItem, epub, lang, axeRunner) { } return results; } catch (err) { + console.log(err); winston.debug(`Error when running HTML checks: ${err}`); throw new Error(`Failed to check Content Document '${spineItem.relpath}'`); } diff --git a/packages/ace-http/src/index.js b/packages/ace-http/src/index.js index 104e95d3..3cb78254 100644 --- a/packages/ace-http/src/index.js +++ b/packages/ace-http/src/index.js @@ -16,6 +16,8 @@ const axeRunner = require('@daisy/ace-axe-runner-puppeteer'); const pkg = require('@daisy/ace-meta/package'); +// tmp.setGracefulCleanup(); + const UPLOADS = tmp.dirSync({ unsafeCleanup: true }).name; const DEFAULTPORT = 8000; const DEFAULTHOST = "localhost"; diff --git a/packages/ace-report/src/generate-html-report.js b/packages/ace-report/src/generate-html-report.js index 4909d997..4ba8184b 100644 --- a/packages/ace-report/src/generate-html-report.js +++ b/packages/ace-report/src/generate-html-report.js @@ -8,6 +8,8 @@ const winston = require('winston'); const { localize, getCurrentLanguage } = require('./l10n/localize').localizer; +const LOG_DEBUG_URLS = process.env.LOG_DEBUG_URLS === "1"; + // generate the html report and return it as a string module.exports = function generateHtmlReport(reportData) { @@ -39,14 +41,14 @@ module.exports = function generateHtmlReport(reportData) { var filterOptions = ""; violationFilters[rule].forEach(function(value) { // winston.info("######## " + value); - const valueDisplay = localize(value, {ignoreMissingKey: true}); // only handles "serious", "moderate", etc. so can be missingKey, such as "EPUB/package.opf", "color-contrast", "metadata-schema-accessibilitysummary" etc. (in which case => fallback to key string) + const valueDisplay = rule == "file" ? value : localize(value, {ignoreMissingKey: true}); // only handles "serious", "moderate", etc. so can be missingKey, such as "EPUB/package.opf", "color-contrast", "metadata-schema-accessibilitysummary" etc. (in which case => fallback to key string) // use nicer labels for ruleset options if (rule == "ruleset") { filterOptions += ""; } else { filterOptions += ""; } }); @@ -66,7 +68,7 @@ module.exports = function generateHtmlReport(reportData) { ${valueDisplay} ${rulesetTagLabels[violation['applicableRulesetTag']]} ${violation['rule']}

${violation['engine']} - \"${violation['fileTitle']}\"

${violation['location']}`; + \"${violation['fileTitle']}\"

${escape(violation['location'])}`; if (violation.html) { htmlStr +=`

${localize("snippet")}${violation.html.trim()}
`; @@ -131,6 +133,36 @@ module.exports = function generateHtmlReport(reportData) { return new handlebars.SafeString(valueDisplay); }); + handlebars.registerHelper('encodeURI', function(src, options) { + // console.log(JSON.stringify(options)); + + if (LOG_DEBUG_URLS) { + console.log("///// Mustache encodeURI 1"); + console.log(src); + } + const url = escape(encodeURI(src)); + if (LOG_DEBUG_URLS) { + console.log("///// Mustache encodeURI 2"); + console.log(url); + } + return new handlebars.SafeString(url); + }); + + handlebars.registerHelper('decodeURI', function(url, options) { + // console.log(JSON.stringify(options)); + + if (LOG_DEBUG_URLS) { + console.log("///// Mustache decodeURI 1"); + console.log(url); + } + const src = escape(decodeURI(url)); + if (LOG_DEBUG_URLS) { + console.log("///// Mustache decodeURI 2"); + console.log(src); + } + return new handlebars.SafeString(src); + }); + const content = fs.readFileSync(path.join(__dirname, "./report-template.handlebars")).toString(); var template = handlebars.compile(content); var result = template(reportData); diff --git a/packages/ace-report/src/report-template.handlebars b/packages/ace-report/src/report-template.handlebars index ecb3fdbe..1da35e3e 100644 --- a/packages/ace-report/src/report-template.handlebars +++ b/packages/ace-report/src/report-template.handlebars @@ -413,8 +413,8 @@ {{#each data.images}} - - + + @@ -436,7 +436,7 @@ {{#localize "na"}}{{/localize}} {{/if}} - {{location}} + {{#decodeURI location}}{{/decodeURI}} {{#if role}} {{role}} diff --git a/packages/epub-utils/package.json b/packages/epub-utils/package.json index 7adf24be..3ffb87fc 100644 --- a/packages/epub-utils/package.json +++ b/packages/epub-utils/package.json @@ -19,7 +19,9 @@ "main": "lib/index.js", "dependencies": { "extract-zip": "^1.6.7", + "file-url": "^3.0.0", "fs-extra": "^8.1.0", + "node-stream-zip": "^1.8.2", "tmp": "^0.1.0", "winston": "^3.2.1", "xmldom-alpha": "^0.1.28", diff --git a/packages/epub-utils/src/epub-parse.js b/packages/epub-utils/src/epub-parse.js index bbd2b9ee..9f706203 100644 --- a/packages/epub-utils/src/epub-parse.js +++ b/packages/epub-utils/src/epub-parse.js @@ -10,6 +10,7 @@ 'use strict'; +const fileUrl = require('file-url'); const DOMParser = require('xmldom-alpha').DOMParser; const XMLSerializer = require('xmldom-alpha').XMLSerializer; const fs = require('fs'); @@ -104,7 +105,7 @@ function addLink(rel, href, link) { function parseLinks(doc, select) { const result = {}; select('//opf:link[not(@refines)]', doc).forEach((link) => { - addLink(link.getAttribute('rel'), link.getAttribute('href'), result); + addLink(link.getAttribute('rel'), decodeURI(link.getAttribute('href')), result); }); return result; } @@ -149,10 +150,14 @@ EpubParser.prototype.parseData = function(packageDocPath, epubDir) { const contentType = (manifestItem[0].getAttribute('media-type')||'').trim(); if (this.contentDocMediaType === contentType) { var spineItem = new SpineItem(); - spineItem.relpath = manifestItem[0].getAttribute('href'); + spineItem.relpath = decodeURI(manifestItem[0].getAttribute('href')); spineItem.filepath = path.join(path.dirname(packageDocPath), spineItem.relpath); spineItem.title = this.parseContentDocTitle(spineItem.filepath); - spineItem.url = "file://" + spineItem.filepath; + + // does encodeURI() as per https://tools.ietf.org/html/rfc3986#section-3.3 in a nutshell: encodeURI(`file://${tmpFile}`).replace(/[?#]/g, encodeURIComponent) + spineItem.url = fileUrl(spineItem.filepath); + // spineItem.url = "file://" + encodeURI(spineItem.filepath); + this.contentDocs.push(spineItem); } else if (!this.hasSVGContentDocuments && 'image/svg+xml' === contentType) { winston.warn('The SVG Content Documents in this EPUB will be ignored.'); @@ -165,7 +170,7 @@ EpubParser.prototype.parseData = function(packageDocPath, epubDir) { + '[contains(concat(" ", normalize-space(@properties), " ")," nav ")]' + '/@href', doc); if (navDocRef.length > 0) { - const navDocPath = navDocRef[0].nodeValue; + const navDocPath = decodeURI(navDocRef[0].nodeValue); const navDocFullPath = path.join(path.dirname(packageDocPath), navDocPath); this.navDoc = parseNavDoc(navDocFullPath, epubDir); } @@ -196,7 +201,7 @@ EpubParser.prototype.calculatePackageDocPath = function(epubDir) { const rootfiles = select('//ocf:rootfile[@media-type="application/oebps-package+xml"]/@full-path', doc); // just grab the first one as we're not handling the case of multiple renditions if (rootfiles.length > 0) { - return (path.join(epubDir, rootfiles[0].nodeValue)); + return (path.join(epubDir, decodeURI(rootfiles[0].nodeValue))); } return ''; } diff --git a/packages/epub-utils/src/epub.js b/packages/epub-utils/src/epub.js index 0025f75d..1fba3ca7 100644 --- a/packages/epub-utils/src/epub.js +++ b/packages/epub-utils/src/epub.js @@ -1,6 +1,7 @@ 'use strict'; const epubParse = require('./epub-parse.js'); +const StreamZip = require('node-stream-zip'); const extractZip = require('extract-zip'); const tmp = require('tmp'); const fs = require('fs-extra'); @@ -9,17 +10,64 @@ const winston = require('winston'); tmp.setGracefulCleanup(); -async function unzip(path) { - const tmpdir = tmp.dirSync({ unsafeCleanup: true }).name; +const LOG_DEBUG_URLS = process.env.LOG_DEBUG_URLS === "1"; + +async function unzip(path, useLegacyZipLib) { + const tmpdir = tmp.dirSync({ unsafeCleanup: true, keep: LOG_DEBUG_URLS }).name; + if (LOG_DEBUG_URLS) { + console.log(">>>>>> LOG_DEBUG_URLS"); + console.log(path); + console.log(tmpdir); + } return new Promise((resolve, reject) => { - extractZip(path, { dir: tmpdir }, (err) => { - if (err) { + if (useLegacyZipLib) { + extractZip(path, { dir: tmpdir }, (err) => { + if (err) { + if (LOG_DEBUG_URLS) { + console.log(err); + } + reject(err); + } else { + resolve(tmpdir); + } + }); + } else { + const zip = new StreamZip({ + file: path, + storeEntries: true, // zip.entries() zip.entriesCount (necessary for zip.extract()) + }); + zip.on('error', (err) => { + if (LOG_DEBUG_URLS) { + console.log(err); + } reject(err); - } else { - resolve(tmpdir); + }); + zip.on('ready', () => { + zip.extract(null, tmpdir, (err, count) => { + if (LOG_DEBUG_URLS) { + console.log(`ZIP COUNT ${count}`); + } + zip.close(); + if (err) { + if (LOG_DEBUG_URLS) { + console.log(err); + } + reject(err); + } else { + resolve(tmpdir); + } + }); + }); + if (LOG_DEBUG_URLS) { + zip.on('extract', (entry, file) => { + console.log(`ZIP EXTRACT ${entry.name} to ${file}`); + }); + zip.on('entry', (entry) => { + console.log(`ZIP ENTRY ${entry.name}`); + }); } - }); - }) + } + }); } async function retryUnzip(epub, error) { @@ -48,7 +96,7 @@ async function retryUnzip(epub, error) { } } fs.truncateSync(tmpEPUB, truncatedSize); - const res = await unzip(tmpEPUB); + const res = await unzip(tmpEPUB, true); if (needsDelete) { process.nextTick(() => { fs.unlink(tmpEPUB); @@ -92,12 +140,18 @@ class EPUB { try { unzippedDir = await unzip(this.path); } catch (error) { - winston.error('Failed to unzip EPUB (the ZIP archive may be corrupt).'); + winston.error('Failed to unzip EPUB (the ZIP archive may be corrupt). TRYING LEGACY ZIP LIB ...'); winston.debug(error); try { - unzippedDir = await retryUnzip(this, error); + unzippedDir = await unzip(this.path, true); } catch (error) { - throw error; + winston.error('Failed to unzip EPUB again (the ZIP archive may be corrupt). TRYING ZIP PATCH ...'); + winston.debug(error); + try { + unzippedDir = await retryUnzip(this, error); + } catch (error) { + throw error; + } } } this.basedir = unzippedDir; diff --git a/yarn.lock b/yarn.lock index b40b1906..9a500bb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1732,9 +1732,9 @@ universal-user-agent "^4.0.0" "@octokit/types@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-1.0.0.tgz#13d2361123cb06bead36ba836b0639c5cbd15add" - integrity sha512-u51RhPTdCJgZQnU4TuKiqHcAxINsvIkQDZdbF4wSJy3g+DH7X/SmYp1kJE6INRD8hh2wEeFmRke7h1j6Ed3e+w== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-1.1.0.tgz#6c9b286f9766f8cc6c5bab9fd3eb6a7aa019c586" + integrity sha512-t4ZD74UnNVMq6kZBDZceflRKK3q4o5PoCKMAGht0RK84W57tqonqKL3vCxJHtbGExdan9RwV8r7VJBZxIM1O7Q== dependencies: "@types/node" "^12.11.1" @@ -2832,7 +2832,7 @@ browserslist@^3.2.6: caniuse-lite "^1.0.30000844" electron-to-chromium "^1.3.47" -browserslist@^4.6.0, browserslist@^4.7.1: +browserslist@^4.6.0, browserslist@^4.7.2: version "4.7.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.2.tgz#1bb984531a476b5d389cedecb195b2cd69fb1348" integrity sha512-uZavT/gZXJd2UTi9Ov7/Z340WOSQ3+m1iBVRUknf+okKxonL9P83S3ctiBDtuRmRu8PiCHjqyueqQ9HYlJhxiw== @@ -3535,11 +3535,11 @@ copy-descriptor@^0.1.0: integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= core-js-compat@^3.1.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.3.3.tgz#82642808cf484a35292b2f8e83ef9376884e760f" - integrity sha512-GNZkENsx5pMnS7Inwv7ZO/s3B68a9WU5kIjxqrD/tkNR8mtfXJRk8fAKRlbvWZSGPc59/TkiOBDYl5Cb65pTVA== + version "3.3.4" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.3.4.tgz#a151c6cd754edbfe6a4a2a66b9382df2ae74fbcd" + integrity sha512-7OK3/LPP8R3Ovasf3GilEOp+o1w0ZKJ75FMou2RDfTwIV69G5RkKCGFnqgBv/ZhR6xo9GCzlfVALyHmydbE7DA== dependencies: - browserslist "^4.7.1" + browserslist "^4.7.2" semver "^6.3.0" core-js@^2.4.0, core-js@^2.5.0: @@ -3960,9 +3960,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.295, electron-to-chromium@^1.3.47: - version "1.3.295" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.295.tgz#4727eabfa2642f9b21c43ec17d794c004724657b" - integrity sha512-KxlGE9GcZTv7xGwYJGMEABHJq2JuTMNF7jD8NwHk6sBY226mW+Dyp9kZmA2Od9tKHMCS7ltPnqFg+zq3jTWN7Q== + version "1.3.296" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.296.tgz#a1d4322d742317945285d3ba88966561b67f3ac8" + integrity sha512-s5hv+TSJSVRsxH190De66YHb50pBGTweT9XGWYu/LMR20KX6TsjFzObo36CjVAzM+PUeeKSBRtm/mISlCzeojQ== electron@^7.0.0: version "7.0.0" @@ -4937,9 +4937,9 @@ http-signature@~1.2.0: sshpk "^1.7.0" https-proxy-agent@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.3.tgz#fb6cd98ed5b9c35056b5a73cd01a8a721d7193d1" - integrity sha512-Ytgnz23gm2DVftnzqRRz2dOXZbGd2uiajSw/95bPp6v53zPRspQjLm/AfBgqbJ2qfeRXWIOMVLpp86+/5yX39Q== + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== dependencies: agent-base "^4.3.0" debug "^3.1.0" @@ -6795,12 +6795,17 @@ node-pre-gyp@^0.12.0: tar "^4" node-releases@^1.1.38: - version "1.1.38" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.38.tgz#d81b365df2936654ba37f509ba2fbe91eff2578b" - integrity sha512-/5NZAaOyTj134Oy5Cp/J8mso8OD/D9CSuL+6TOXXsTKO8yjc5e4up75SRPCganCjwFKMj2jbp5tR0dViVdox7g== + version "1.1.39" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.39.tgz#c1011f30343aff5b633153b10ff691d278d08e8d" + integrity sha512-8MRC/ErwNCHOlAFycy9OPca46fQYUjbJRDcZTHVWIGXIjYLM73k70vv3WkYutVnM4cCo4hE0MqBVVZjP6vjISA== dependencies: semver "^6.3.0" +node-stream-zip@^1.8.2: + version "1.8.2" + resolved "https://registry.yarnpkg.com/node-stream-zip/-/node-stream-zip-1.8.2.tgz#1f79e30ab3ff54cbda312cd3a9f0030b15bb3f53" + integrity sha512-zwP2F/R28Oqtl0gOLItk5QjJ6jEU8XO4kaUMgeqvCyXPgdCZlm8T/5qLMiNy+moJCBCiMQAaX7aVMRhT0t2vkQ== + "nopt@2 || 3": version "3.0.6" resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"