├── package.json ├── module_random.js ├── module_array.js ├── README_english.md ├── README.md ├── module_mail.js ├── index.js ├── module_pu_hackerone.js └── yarn.lock /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vulspiderX", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "dependencies": { 7 | "nodemailer": "^6.6.1", 8 | "puppeteer": "^1.14.0" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /module_random.js: -------------------------------------------------------------------------------- 1 | // 伪随机数范围 [low, high] 2 | // 中括号表示“包括” 即结果可能为最小和最大值 3 | function randomIntInc(low, high) { 4 | return Math.floor(Math.random() * (high - low + 1) + low) 5 | } 6 | 7 | //暴露函数 让index.js调用 8 | module.exports.randomIntInc = randomIntInc; 9 | -------------------------------------------------------------------------------- /module_array.js: -------------------------------------------------------------------------------- 1 | 2 | function arraysEqual(a, b) { 3 | if (a === b) return true; 4 | if (a == null || b == null) return false; 5 | if (a.length != b.length) return false; 6 | 7 | for (var i = 0; i < a.length; ++i) { 8 | if (a[i] !== b[i]) return false; 9 | } 10 | return true; 11 | } 12 | 13 | 14 | //求两个数组的差集(特殊的是 这两个数组中的元素 也是数组) 15 | //res1 - res2 16 | function getdifference(res1, res2) { 17 | for (i = 0; i < res1.length; i++) { 18 | for (j = 0; j < res2.length; j++) { 19 | if (arraysEqual(res1[i], res2[j])) { 20 | res1.splice(i, 1);//删除1个 索引为i 的数组元素 21 | } 22 | } 23 | } 24 | return res1 25 | } 26 | 27 | 28 | //暴露函数 让index.js调用 29 | module.exports.getdifference = getdifference; -------------------------------------------------------------------------------- /README_english.md: -------------------------------------------------------------------------------- 1 | # VulSpiderX 2 | 3 | [english](README_english.md) | [中文文档](README.md) 4 | 5 | It runs in the background and gets the latest information on Hackerone every 10 to 20 minutes (random). If there are the latest vulnerabilities, it sends emails (title + URL links) to security researchers actively. 6 | 7 | The crawl function uses Google puppeteer module to manipulate headless chromium to dynamically parse javascript bypass the anti-crawl mechanism. 8 | 9 | ### Screenshots 10 | 11 | ![all](https://github.com/1135/notes/blob/master/imgs/vulspiderX.png?raw=true) 12 | Recipient privacy security (any recipient cannot get the email addresses of other recipients) 13 | 14 | ### Configuration 15 | 16 | Edit `module_mail.js` 17 | 18 | * Configure the mail sender information 19 | * `host:` E-mail host address 20 | * `user =` E-mail account 21 | * `pass =` E-mail password 22 | 23 | * Configure recipient address 24 | * `recipients =` recipient address 25 | 26 | ### Usage 27 | 28 | >Please ensure that you have installed node.js and the package manager yarn(I use the environment Mac OS + node v10.15.3). 29 | >Other environments are not actually tested yet. 30 | 31 | ``` 32 | # Install dependent libraries 33 | yarn install 34 | 35 | # Run (save the log) 36 | node index.js > VulSpiderX.log 37 | ``` 38 | 39 | ### Related projects 40 | 41 | * [1135/VulSpider](https://github.com/1135/VulSpider) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VulSpiderX 2 | 3 | [english](README_english.md) | [中文文档](README.md) 4 | 5 | 功能说明: 本程序在后台运行,每10到20分钟(随机)获取Hackerone的最新信息,如果有最新的漏洞,主动发送电子邮件(标题+链接)给若干个安全研究人员。 6 | 7 | 技术说明: 本程序使用node.js开发,爬取功能使用了Google puppeteer模块来模拟人工操作headless chromium,同时动态解析javascript,以绕过反爬机制。 8 | 9 | 10 | ### 实现效果 11 | 12 | ![all](https://github.com/1135/notes/blob/master/imgs/vulspiderX.png?raw=true) 13 | 14 | 收件人隐私安全(任一收件人无法得到其他收件人的邮箱地址) 15 | 16 | ### 配置参数 17 | 18 | 编辑`module_mail.js`进行配置 19 | 20 | * 配置发件人信息 21 | * `host: ` 邮箱服务地址 22 | * `port: ` 邮件服务端口 建议使用SMTP-over-SSL协议(通常为465端口) 不建议使用SMTP协议(通常为25端口) 23 | * `secureConnection: ` SMTP-over-SSL协议则设置为true 否则为false 24 | * `user =` 邮箱账号 25 | * `pass =` 邮箱密码 26 | 27 | * 配置收件人信息 28 | * `recipients =`收件人邮箱地址列表 29 | 30 | ### 使用方法 31 | 32 | 请确保您已经安装了 node.js 和 包管理器yarn 33 | ```shell 34 | # 自动安装本程序的依赖库 35 | yarn install 36 | 37 | # 运行 可选择保存程序日志 38 | node index.js > VulSpiderX.log 39 | ``` 40 | 41 | * 运行环境 42 | * 环境1: macOS 10.14.4 + node v10.15.3 亲测通过 ✅ 43 | * 环境2: CentOS 7 + node v11.0.0 亲测通过 ✅ 如果报错请安装你缺少的chromium的依赖库即可 如`yum install libXScrnSaver*` 更多参考[puppeteer/troubleshooting.md](https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-on-google-cloud-functions) 44 | * 其他: 暂未测试 45 | 46 | ### 更新日志 47 | 48 | 2019.4 开发完成. 2019-2024.2持续可用. 49 | 50 | 2024.4 更新完成. Hackerone官网前端页面发生变更, 已修改相关提取数据的代码, 已更新到这个公开仓库. 51 | 52 | 53 | ### 相关项目 54 | 55 | * [1135/VulSpider: 本程序在后台持续运行,获取最新漏洞及每日简报,发送邮件给安全人员。](https://github.com/1135/VulSpider) 56 | -------------------------------------------------------------------------------- /module_mail.js: -------------------------------------------------------------------------------- 1 | var nodemailer = require('nodemailer'); 2 | 3 | var user = 'xxxx@aliyun.com';//发件 账号 4 | var pass = 'xxxx';//发件 密码 5 | var recipients = "1@sohu.com, 2@126.com";//收件地址 列表 6 | 7 | 8 | async function send_a_mail(title_of_mail, content_of_mail , one_pdf_file) { 9 | 10 | var transporter = nodemailer.createTransport({ 11 | host: "smtp.aliyun.com", 12 | //port: 25, // SMTP 13 | port: 465, // SMTPS(SMTP-over-SSL) 14 | secureConnection: true, // SSL 15 | auth: { 16 | user: user, 17 | pass: pass, 18 | } 19 | }); 20 | 21 | // 发送邮件的配置信息 - 不带附件 22 | var mailOptions = { 23 | from: "VulX <" + user + ">", // 发件人 24 | //to: recipients, 25 | bcc: recipients,//bcc是密送 26 | subject: title_of_mail, // 标题 27 | //text和html两者只支持一种 28 | text: content_of_mail, // 内容 29 | //html: 'Hello world ?' // html 内容 30 | }; 31 | 32 | 33 | // 发送邮件的配置信息 - 带附件 34 | var mailOptions_with_attachment = { 35 | from: "VulX <" + user + ">", // 发件人 36 | //to: recipients, 37 | bcc: recipients, 38 | subject: title_of_mail, // 标题 39 | //text和html两者只支持一种 40 | text: content_of_mail, // 内容 41 | //html: 'Hello world ?' // html 内容 42 | attachments: [ 43 | // { // utf-8 string as an attachment 44 | // filename: 'text1.txt', 45 | // content: 'hello world!' 46 | // }, 47 | 48 | { // binary buffer as an attachment 49 | filename: title_of_mail + '.pdf', 50 | content: one_pdf_file 51 | }] 52 | }; 53 | 54 | 55 | 56 | function callbackit(error, info) { 57 | if (error) { 58 | console.log(error) 59 | return console.log(error); 60 | // console.log(error) 61 | // transporter.sendMail(mailOptions,callbackit)//重新发送 62 | // return console.log("[mail]fail! retry..."+ mailOptions.subject + mailOptions.text) 63 | } 64 | console.log('Message sent: ' + info.response); 65 | 66 | } 67 | 68 | if(one_pdf_file == -1 ){ 69 | //无需附件 70 | // send mail with defined transport object 71 | await transporter.sendMail(mailOptions, callbackit); 72 | 73 | } 74 | else 75 | { //需要附件 76 | // send mail with defined transport object 77 | await transporter.sendMail(mailOptions_with_attachment, callbackit); 78 | } 79 | 80 | } 81 | 82 | 83 | //暴露函数 让index.js调用 84 | module.exports.send_a_mail = send_a_mail; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const module_array = require('./module_array'); 2 | const module_mail = require('./module_mail'); 3 | const module_pu_hackerone = require('./module_pu_hackerone'); 4 | const moudule_random = require('./module_random') 5 | 6 | var oldarray = []; 7 | var result_array = []; 8 | var diffarray = []; 9 | var crawl_count = 0; 10 | 11 | 12 | //定义sleep函数 13 | function sleep(ms) { 14 | return new Promise(resolve => setTimeout(resolve, ms || 1000)); 15 | } 16 | 17 | // 输出当前时间 18 | function print_current_time() { 19 | var date = new Date(); 20 | var year = date.getFullYear(); 21 | var month = date.getMonth() + 1; 22 | var day = date.getDate(); 23 | var hour = date.getHours(); 24 | var minute = date.getMinutes(); 25 | var second = date.getSeconds(); 26 | console.log(year + '.' + month + '.' + day + ' ' + hour + ':' + minute + ':' + second); 27 | 28 | } 29 | 30 | 31 | 32 | //发送邮件 逐条发送 33 | async function sendmail(arr) { 34 | console.log("-----[mail]sending") 35 | 36 | arr.reverse();//数组逆序 以便发送后邮件显示顺序为由新到旧 第一条最新 37 | 38 | for (var index = 0; index < arr.length; index++) { 39 | 40 | let one_title = arr[index][0]; 41 | let one_content = arr[index][1]; 42 | await module_mail.send_a_mail(one_title, one_content);// 漏洞标题 漏洞url 43 | await sleep(20 * 1000);//20秒后继续发送下一封邮件 避免ban 44 | } 45 | 46 | console.log("-----[mail]done") 47 | } 48 | 49 | 50 | async function mainlogic() { 51 | 52 | //爬取 计时开始 53 | const startDate = new Date().getTime(); 54 | 55 | //爬取次数 第几次爬取 56 | crawl_count += 1; 57 | 58 | console.log('-----------------------------------'); 59 | 60 | 61 | console.log(`[crawl]times: ${crawl_count}`);//爬取次数 62 | 63 | result_array = await module_pu_hackerone.crawl(); 64 | 65 | //爬取 计时结束 66 | console.log(`[crawl] Time elapsed ${Math.round((new Date().getTime() - startDate) / 1000)} s`); 67 | 68 | //console.log(result_array) 69 | if (result_array != null) {//res不为空 70 | console.log('items count: ', result_array.length); 71 | //console.log(`\n[crawl]data: ${res}`);//本次爬取的数据 72 | 73 | 74 | if (crawl_count == 1) {//首次爬取 75 | // console.log("first") 76 | console.log(result_array) 77 | 78 | //逐条发送 可能因为一次性发送多封邮件被ban 79 | //sendmail(res); 80 | 81 | var result = "The program runs successfully.\nHere are the results of the first crawl.\nThe email address will receive the latest information in the future.\n\n-----\n\n"; 82 | 83 | for (var index = 0; index < result_array.length; index++) { 84 | let one_title = result_array[index][0]; 85 | let one_content = result_array[index][1]; 86 | result += one_title + "\n" + one_content + "\n\n========\n\n" // 换行 87 | } 88 | 89 | 90 | 91 | sendmail([["Running...", result]]); 92 | 93 | oldarray = result_array.concat()//深拷贝 数组 94 | 95 | } 96 | else { 97 | 98 | //求差集 99 | //result_array - oldarray 100 | diffarray = module_array.getdifference(result_array.concat(), oldarray) 101 | 102 | if (diffarray.length > 0) {//如果有新增内容 103 | console.log('diff:', diffarray);//新增的数量 104 | 105 | console.log("\nall:") 106 | console.log(result_array) 107 | if (diffarray.length < 9){ 108 | sendmail(diffarray); 109 | } 110 | else{ 111 | console.log('skip') 112 | } 113 | oldarray = result_array.concat()//深拷贝 数组 114 | 115 | } 116 | else {//没有新增内容 117 | console.log("diff: 0") 118 | } 119 | 120 | } 121 | 122 | } 123 | else { 124 | console.log("it is null.") 125 | } 126 | 127 | console.log('-----------------------------------'); 128 | 129 | } 130 | 131 | 132 | async function app() { 133 | 134 | while (1) { 135 | await mainlogic(); 136 | print_current_time() 137 | let wait_time = moudule_random.randomIntInc(10, 20) * 60 * 1000; 138 | console.log("Wait " + wait_time / (60 * 1000) + " minutes for the next crawl."); 139 | await sleep(wait_time);//每两次爬取之间的时间间隔为10-20分钟 140 | } 141 | 142 | } 143 | 144 | app() 145 | -------------------------------------------------------------------------------- /module_pu_hackerone.js: -------------------------------------------------------------------------------- 1 | const puppeteer = require('puppeteer'); 2 | 3 | 4 | //模拟移动设备 默认支持列表 https://github.com/GoogleChrome/puppeteer/blob/master/DeviceDescriptors.js 5 | //const devices = require('puppeteer/DeviceDescriptors'); 6 | //const iPhone = devices['iPhone X']; 7 | 8 | 9 | async function crawl() { 10 | 11 | //const puppeteer = require('puppeteer'); 12 | 13 | //模拟移动设备 默认支持列表 https://github.com/GoogleChrome/puppeteer/blob/master/DeviceDescriptors.js 14 | //const devices = require('puppeteer/DeviceDescriptors'); 15 | //const iPhone = devices['iPhone X']; 16 | 17 | 18 | 19 | //运行浏览器 20 | const browser = await 21 | puppeteer.launch({ 22 | //headless: false,// false则显示浏览器窗口 23 | //devtools: true, //是否为每个选项卡自动打开DevTools面板 (该选项只有当 headless 选项为 false 的时候有效) 24 | slowMo: 250,//浏览器每个操作的间隔时间 慢下来以便观察 slow down by 250ms 25 | defaultViewport: { width: 1440, height: 821 },//修改viewport为mbp15下用chrome时的大小 //默认viewport为 800x600 26 | ignoreHTTPSErrors: true, //忽略https错误 27 | args: [ 28 | // browser proxy 29 | //'--proxy-server=127.0.0.1:8080', 30 | 31 | //设置整个浏览器的user-agent为 Mac+chrome 32 | '--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36', 33 | 34 | // 已知1: 如果在 mac系统下 开启chromium沙箱 正常运行. 35 | // 已知2: 如果在 linux和windows系统下 开启chromium沙箱 可能兼容性不佳 要么关闭沙箱 要么尝试使用参数 --enable-features=UseOzonePlatform 36 | 37 | //'--no-sandbox', 38 | //'--disable-setuid-sandbox', 39 | ], 40 | }); 41 | 42 | 43 | let page1 = await browser.newPage(); 44 | //await page.emulate(iPhone); 45 | let url1 = 'https://hackerone.com/hacktivity/overview?queryString=disclosed%3Atrue&sortField=latest_disclosable_activity_at&sortDirection=DESC&pageIndex=0'; 46 | 47 | 48 | 49 | /* 50 | 删除部分代码: 51 | 由于新版本的hackerone网站(2024年) 已经不是懒加载了 所以不需要 滚动/向下翻页 了. 52 | */ 53 | 54 | 55 | 56 | 57 | //--------------------- 58 | try { 59 | 60 | // 启动浏览器 61 | await page1.evaluateOnNewDocument(() => { 62 | Object.defineProperty(navigator, 'webdriver', { 63 | get: () => undefined, 64 | }); 65 | }) 66 | 67 | // 跳转配置 68 | await 69 | page1.goto( 70 | url1, 71 | { 72 | waitUntil: 'networkidle2',//networkidle2 表示500毫秒内 没有 多于2个网络连接时 结束navigation导航 //networkidle0表示500毫秒内 没有任何网络连接时 结束navigation导航 73 | timeout: 400 * 1000,//最大导航时间(以毫秒为单位),默认为30秒,传递0表示禁用超时(disable timeout) 一直等待(好像是600秒) 74 | referer: 'https://www.hackerone.com/', //请求头中加入referer 75 | } 76 | ); 77 | 78 | 79 | 80 | //等待页面内容的加载. 81 | await page1.waitForSelector('input[data-testid="hacktivity-search-input-input-input"]'); //等待具有 name="search" 属性的 元素加载到 DOM 中,并且该元素是可见的和可交互的。 82 | 83 | 84 | 85 | 86 | 87 | 88 | //-----提取内容 89 | 90 | //在浏览器console调试 91 | // 获取漏洞标题 92 | // 筛选1 document.querySelectorAll('div.spec-hacktivity-content>a')[0].innerText 93 | // 说明 因为未公开的漏洞标题 没有a标签 所以这个筛选只能找到不被隐藏的标题。 无法找到隐藏的标题 94 | 95 | // 筛选2 document.querySelectorAll('div.spec-hacktivity-content')[16].outerText 96 | // 如果 隐藏 不能被展示 爬到的内容为 "closed 2 hrs agoBy batee5a to InnoGames$300.00" 97 | 98 | 99 | // 获取漏洞链接 100 | // 筛选1 document.querySelectorAll('div.spec-hacktivity-content>a')[0].href 101 | 102 | let eleCount = await page1.evaluate((sel) => { 103 | // return document.querySelectorAll('div.spec-hacktivity-content>a').length; 104 | return document.querySelectorAll('div[data-testid="hacktivity-item"]').length; 105 | }); 106 | 107 | 108 | //输出爬取的 元素条数 数据量 109 | console.log(eleCount); 110 | 111 | var result;//声明变量 112 | if (eleCount != 0) { 113 | result = await page1.evaluate((sel, eleCount) => { 114 | let element = document.querySelectorAll(sel);//获取文档中所有 'div.react-flex-view>a' 元素的NodeList 115 | console.log(element); 116 | 117 | let tempArray1 = [];//定义空数组 118 | 119 | 120 | for (let i = 0; i <= eleCount - 1; i++) { 121 | console.log(i); 122 | // console.log(element[i]?.innerText); 123 | 124 | 125 | const one_item = { 126 | title: element[i].querySelector('div[data-testid="report-title"]')?.innerText, 127 | severity: element[i].querySelector('span[data-testid="report-severity"]')?.innerText, 128 | reward奖金: element[i].querySelector('span[data-testid="report-reward"]')?.innerText, 129 | status: element[i].querySelector('div[data-testid="report-status"]')?.innerText, 130 | summary: element[i].querySelector('div[class="interactive-markdown text-sm w-full"]')?.innerText, 131 | link: element[i].querySelector('a[href^="/"]')?.href, 132 | }; 133 | 134 | 135 | 136 | // let one_item_title = element[i].querySelector('div[data-testid="report-title"]')?.innerText; 137 | // let one_item_severity = element[i].querySelector('span[data-testid="report-severity"]')?.innerText; 138 | 139 | // let one_item_reward = element[i].querySelector('span[data-testid="report-reward"]')?.innerText; 140 | 141 | // let one_item_status = element[i].querySelector('div[data-testid="report-status"]')?.innerText; 142 | // let one_item_disclosedAt = element[i].querySelector('div[data-testid="report-disclosed-at"]')?.innerText; 143 | // let one_item_reporter = element[i].querySelector('a[href^="/"]')?.innerText; 144 | // let one_item_summary = element[i].querySelector('div[class="interactive-markdown text-sm w-full"]')?.innerText; 145 | 146 | 147 | // let one_item = [one_item_title, one_item_severity, one_item_reward, one_item_status, one_item_disclosedAt, one_item_reporter, one_item_summary]; 148 | // const oneItemString = one_item.join('\n\n\n'); 149 | 150 | const oneItemString = JSON.stringify(one_item, null, 2) + '\n\n\n🔗 ' + one_item.link; 151 | 152 | tempArray1[i] = [one_item.title, oneItemString] 153 | // element[i].innerText+'\n'+element[i].href;//取出某个元素 的innerText 154 | 155 | } 156 | return tempArray1 157 | }, 'div[data-testid="hacktivity-item"]', eleCount); 158 | 159 | //console.log(result) 160 | 161 | } 162 | } 163 | catch (err) { 164 | console.log(`An error occured.h1_[will_close_browser]`); 165 | await browser.close(); //已经出现异常 那就关闭浏览器 等待下次爬 166 | console.log(err); 167 | 168 | } 169 | finally { 170 | await browser.close(); 171 | return result; 172 | } 173 | 174 | 175 | } 176 | 177 | 178 | //暴露函数 让index.js调用 179 | module.exports.crawl = crawl; 180 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | agent-base@^4.3.0: 6 | version "4.3.0" 7 | resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" 8 | integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== 9 | dependencies: 10 | es6-promisify "^5.0.0" 11 | 12 | async-limiter@~1.0.0: 13 | version "1.0.1" 14 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" 15 | integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== 16 | 17 | balanced-match@^1.0.0: 18 | version "1.0.2" 19 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" 20 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== 21 | 22 | brace-expansion@^1.1.7: 23 | version "1.1.11" 24 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 25 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== 26 | dependencies: 27 | balanced-match "^1.0.0" 28 | concat-map "0.0.1" 29 | 30 | buffer-from@^1.0.0: 31 | version "1.1.1" 32 | resolved "http://registry.npm.taobao.org/buffer-from/download/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" 33 | integrity sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8= 34 | 35 | concat-map@0.0.1: 36 | version "0.0.1" 37 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 38 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== 39 | 40 | concat-stream@1.6.2: 41 | version "1.6.2" 42 | resolved "http://registry.npm.taobao.org/concat-stream/download/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 43 | integrity sha1-kEvfGUzTEi/Gdcd/xKw9T/D9GjQ= 44 | dependencies: 45 | buffer-from "^1.0.0" 46 | inherits "^2.0.3" 47 | readable-stream "^2.2.2" 48 | typedarray "^0.0.6" 49 | 50 | core-util-is@~1.0.0: 51 | version "1.0.2" 52 | resolved "http://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 53 | integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= 54 | 55 | debug@2.6.9: 56 | version "2.6.9" 57 | resolved "http://registry.npm.taobao.org/debug/download/debug-2.6.9.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 58 | integrity sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8= 59 | dependencies: 60 | ms "2.0.0" 61 | 62 | debug@^3.1.0: 63 | version "3.2.6" 64 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" 65 | integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== 66 | dependencies: 67 | ms "^2.1.1" 68 | 69 | debug@^4.1.0: 70 | version "4.1.1" 71 | resolved "http://registry.npm.taobao.org/debug/download/debug-4.1.1.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Fdebug%2Fdownload%2Fdebug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 72 | integrity sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E= 73 | dependencies: 74 | ms "^2.1.1" 75 | 76 | es6-promise@^4.0.3: 77 | version "4.2.8" 78 | resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" 79 | integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== 80 | 81 | es6-promisify@^5.0.0: 82 | version "5.0.0" 83 | resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" 84 | integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= 85 | dependencies: 86 | es6-promise "^4.0.3" 87 | 88 | extract-zip@^1.6.6: 89 | version "1.6.7" 90 | resolved "http://registry.npm.taobao.org/extract-zip/download/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" 91 | integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= 92 | dependencies: 93 | concat-stream "1.6.2" 94 | debug "2.6.9" 95 | mkdirp "0.5.1" 96 | yauzl "2.4.1" 97 | 98 | fd-slicer@~1.0.1: 99 | version "1.0.1" 100 | resolved "http://registry.npm.taobao.org/fd-slicer/download/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65" 101 | integrity sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU= 102 | dependencies: 103 | pend "~1.2.0" 104 | 105 | fs.realpath@^1.0.0: 106 | version "1.0.0" 107 | resolved "http://registry.npm.taobao.org/fs.realpath/download/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 108 | integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= 109 | 110 | glob@^7.1.3: 111 | version "7.1.3" 112 | resolved "http://registry.npm.taobao.org/glob/download/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 113 | integrity sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE= 114 | dependencies: 115 | fs.realpath "^1.0.0" 116 | inflight "^1.0.4" 117 | inherits "2" 118 | minimatch "^3.0.4" 119 | once "^1.3.0" 120 | path-is-absolute "^1.0.0" 121 | 122 | https-proxy-agent@^2.2.1: 123 | version "2.2.4" 124 | resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" 125 | integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== 126 | dependencies: 127 | agent-base "^4.3.0" 128 | debug "^3.1.0" 129 | 130 | inflight@^1.0.4: 131 | version "1.0.6" 132 | resolved "http://registry.npm.taobao.org/inflight/download/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 133 | integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= 134 | dependencies: 135 | once "^1.3.0" 136 | wrappy "1" 137 | 138 | inherits@2, inherits@^2.0.3, inherits@~2.0.3: 139 | version "2.0.3" 140 | resolved "http://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 141 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 142 | 143 | isarray@~1.0.0: 144 | version "1.0.0" 145 | resolved "http://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 146 | integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= 147 | 148 | mime@^2.0.3: 149 | version "2.4.2" 150 | resolved "http://registry.npm.taobao.org/mime/download/mime-2.4.2.tgz#ce5229a5e99ffc313abac806b482c10e7ba6ac78" 151 | integrity sha1-zlIppemf/DE6usgGtILBDnumrHg= 152 | 153 | minimatch@^3.0.4: 154 | version "3.1.2" 155 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" 156 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== 157 | dependencies: 158 | brace-expansion "^1.1.7" 159 | 160 | minimist@0.0.8: 161 | version "0.0.8" 162 | resolved "http://registry.npm.taobao.org/minimist/download/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 163 | integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= 164 | 165 | mkdirp@0.5.1: 166 | version "0.5.1" 167 | resolved "http://registry.npm.taobao.org/mkdirp/download/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 168 | integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= 169 | dependencies: 170 | minimist "0.0.8" 171 | 172 | ms@2.0.0: 173 | version "2.0.0" 174 | resolved "http://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 175 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 176 | 177 | ms@^2.1.1: 178 | version "2.1.2" 179 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 180 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 181 | 182 | nodemailer@^6.6.1: 183 | version "6.6.1" 184 | resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.6.1.tgz#2a05fbf205b897d71bf43884167b5d4d3bd01b99" 185 | integrity sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg== 186 | 187 | once@^1.3.0: 188 | version "1.4.0" 189 | resolved "http://registry.npm.taobao.org/once/download/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 190 | integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= 191 | dependencies: 192 | wrappy "1" 193 | 194 | path-is-absolute@^1.0.0: 195 | version "1.0.1" 196 | resolved "http://registry.npm.taobao.org/path-is-absolute/download/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 197 | integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= 198 | 199 | pend@~1.2.0: 200 | version "1.2.0" 201 | resolved "http://registry.npm.taobao.org/pend/download/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" 202 | integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= 203 | 204 | process-nextick-args@~2.0.0: 205 | version "2.0.0" 206 | resolved "http://registry.npm.taobao.org/process-nextick-args/download/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 207 | integrity sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o= 208 | 209 | progress@^2.0.1: 210 | version "2.0.3" 211 | resolved "http://registry.npm.taobao.org/progress/download/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" 212 | integrity sha1-foz42PW48jnBvGi+tOt4Vn1XLvg= 213 | 214 | proxy-from-env@^1.0.0: 215 | version "1.0.0" 216 | resolved "http://registry.npm.taobao.org/proxy-from-env/download/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" 217 | integrity sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4= 218 | 219 | puppeteer@^1.14.0: 220 | version "1.14.0" 221 | resolved "http://registry.npm.taobao.org/puppeteer/download/puppeteer-1.14.0.tgz?cache=0&other_urls=http%3A%2F%2Fregistry.npm.taobao.org%2Fpuppeteer%2Fdownload%2Fpuppeteer-1.14.0.tgz#828c1926b307200d5fc8289b99df4e13e962d339" 222 | integrity sha1-gowZJrMHIA1fyCibmd9OE+li0zk= 223 | dependencies: 224 | debug "^4.1.0" 225 | extract-zip "^1.6.6" 226 | https-proxy-agent "^2.2.1" 227 | mime "^2.0.3" 228 | progress "^2.0.1" 229 | proxy-from-env "^1.0.0" 230 | rimraf "^2.6.1" 231 | ws "^6.1.0" 232 | 233 | readable-stream@^2.2.2: 234 | version "2.3.6" 235 | resolved "http://registry.npm.taobao.org/readable-stream/download/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 236 | integrity sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8= 237 | dependencies: 238 | core-util-is "~1.0.0" 239 | inherits "~2.0.3" 240 | isarray "~1.0.0" 241 | process-nextick-args "~2.0.0" 242 | safe-buffer "~5.1.1" 243 | string_decoder "~1.1.1" 244 | util-deprecate "~1.0.1" 245 | 246 | rimraf@^2.6.1: 247 | version "2.6.3" 248 | resolved "http://registry.npm.taobao.org/rimraf/download/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" 249 | integrity sha1-stEE/g2Psnz54KHNqCYt04M8bKs= 250 | dependencies: 251 | glob "^7.1.3" 252 | 253 | safe-buffer@~5.1.0, safe-buffer@~5.1.1: 254 | version "5.1.2" 255 | resolved "http://registry.npm.taobao.org/safe-buffer/download/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 256 | integrity sha1-mR7GnSluAxN0fVm9/St0XDX4go0= 257 | 258 | string_decoder@~1.1.1: 259 | version "1.1.1" 260 | resolved "http://registry.npm.taobao.org/string_decoder/download/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 261 | integrity sha1-nPFhG6YmhdcDCunkujQUnDrwP8g= 262 | dependencies: 263 | safe-buffer "~5.1.0" 264 | 265 | typedarray@^0.0.6: 266 | version "0.0.6" 267 | resolved "http://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 268 | integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= 269 | 270 | util-deprecate@~1.0.1: 271 | version "1.0.2" 272 | resolved "http://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 273 | integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= 274 | 275 | wrappy@1: 276 | version "1.0.2" 277 | resolved "http://registry.npm.taobao.org/wrappy/download/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 278 | integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= 279 | 280 | ws@^6.1.0: 281 | version "6.2.2" 282 | resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" 283 | integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== 284 | dependencies: 285 | async-limiter "~1.0.0" 286 | 287 | yauzl@2.4.1: 288 | version "2.4.1" 289 | resolved "http://registry.npm.taobao.org/yauzl/download/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" 290 | integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= 291 | dependencies: 292 | fd-slicer "~1.0.1" 293 | --------------------------------------------------------------------------------