diff --git a/index.js b/index.js index 176e02b..6f226d0 100755 --- a/index.js +++ b/index.js @@ -13,7 +13,9 @@ const ui = new UI(); // initialize the UI app .version(require('./package.json').version, '-V, --version') .option('-v, --verbose', 'shows you every single step') - .option('-m, --mute', 'shows only the results of the analysis'); + .option('-u, --audit', 'displays recommandations for further auditing') + .option('-z, --nfz', 'displays informations related to website report operating procedure, AKA NF Z67-147 in France.') + .option('-m, --mute', 'shows only the results of the analysis') app .command('scan [url]') @@ -25,6 +27,7 @@ app .option('-a, --analytics', 'checks for Google Analytics & Piwik') .option('-t, --tracking', 'checks for Social Media tracking & embeds') .option('-c, --cdn', 'checks for Content Delivery Networks') + .option('-k, --cookies [expiration delay, in month]', 'checks for cookies lifetime (< 13 month by defaut)', false) //.option('-r, --recursive', 'tries to follow links to check every internal site', false) .action((url, args) => { // Error Handling @@ -46,9 +49,12 @@ app if (args.parent.mute) ui.set('silent'); // initialize the task runner - const tasks = new Tasks(url, ui); + const tasks = new Tasks(url, ui, args); + if (args.parent.audit) tasks.new('audit'); + if (args.parent.nfz) tasks.new('nfz'); if (args.ssl) tasks.new('ssl'); + if (args.cookies) tasks.new('cookies'); if (args.fonts) tasks.new('fonts'); if (args.prefetching) tasks.new('prefetching'); if (args.analytics) tasks.new('analytics'); @@ -72,4 +78,4 @@ app app.parse(process.argv); -//if (!app.args.length) app.help(); \ No newline at end of file +//if (!app.args.length) app.help(); diff --git a/lib/cookies.js b/lib/cookies.js new file mode 100644 index 0000000..6e6d18d --- /dev/null +++ b/lib/cookies.js @@ -0,0 +1,133 @@ +/*! + * cookies (adaptated from server-side cookies from https://github.com/pillarjs/cookies) + * Copyright(c) 2014 Jed Schmidt, http://jed.is/ + * Copyright(c) 2015-2016 Douglas Christopher Wilson + * Copyright(c) 2018 Baptiste LARVOL-SIMON, http://www.e-glop.net/ + * MIT Licensed + */ + +'use strict' + +var deprecate = require('depd')('cookies') +var Keygrip = require('keygrip') +var cache = {} + +/** + * RegExp to match field-content in RFC 7230 sec 3.2 + * + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + * obs-text = %x80-FF + */ + +var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/; + +/** + * RegExp to match Same-Site cookie attribute value. + */ + +var sameSiteRegExp = /^(?:lax|strict)$/i + +/** + * @param cookies array of arrays of cookies + */ +function Cookies(cookies){ + this.raw_cookies = cookies; +} + +/** + * @return Array + **/ +Cookies.prototype.fetchAll = function() { + var cookies = []; + + if (!this.raw_cookies) return []; + + this.raw_cookies.forEach(cooks => { + cooks.forEach(cook => { + var data = cook.split(/\\{0}; /), + name, + attrs = [], + + name = data.shift().split('='); + + data.forEach(attr => { + var dat = attr.split('='); + attrs[dat[0]] = dat[1]; + }); + + cookies.push(new Cookie(name[0], name[1], attrs)); + }); + }); + + return cookies; +} + +function Cookie(name, value, attrs) { + if (!fieldContentRegExp.test(name)) { + throw new TypeError('argument name is invalid'); + } + + if (value && !fieldContentRegExp.test(value)) { + throw new TypeError('argument value is invalid'); + } + + value || (this.expires = new Date(0)) + + this.name = name + this.value = value || "" + + for (var name in attrs) { + switch ( name ) { + case 'secure': + this[name] = true; + break; + case 'expires': + this[name] = new Date(attrs[name]); + break; + default: + this[name] = attrs[name]; + } + } + + if (this.path && !fieldContentRegExp.test(this.path)) { + throw new TypeError('option path is invalid'); + } + + if (this.domain && !fieldContentRegExp.test(this.domain)) { + throw new TypeError('option domain is invalid'); + } + + if (this.sameSite && this.sameSite !== true && !sameSiteRegExp.test(this.sameSite)) { + throw new TypeError('option sameSite is invalid') + } +} + +Cookie.prototype.path = "/"; +Cookie.prototype.expires = undefined; +Cookie.prototype.domain = undefined; +Cookie.prototype.httpOnly = true; +Cookie.prototype.sameSite = false; +Cookie.prototype.secure = false; +Cookie.prototype.overwrite = false; + +// back-compat so maxage mirrors maxAge +Object.defineProperty(Cookie.prototype, 'maxage', { + configurable: true, + enumerable: true, + get: function () { return this.maxAge }, + set: function (val) { return this.maxAge = val } +}); +deprecate.property(Cookie.prototype, 'maxage', '"maxage"; use "maxAge" instead') + +function getPattern(name) { + if (cache[name]) return cache[name] + + return cache[name] = new RegExp( + "(?:^|;) *" + + name.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&") + + "=([^;]*)" + ) +} + +module.exports = Cookies diff --git a/lib/fonts-parser.js b/lib/fonts-parser.js index 8b3eb1d..2db284e 100644 --- a/lib/fonts-parser.js +++ b/lib/fonts-parser.js @@ -53,6 +53,7 @@ class FontsParser { if (this.getFontStyle(ff) !== '') font.style = this.getFontStyle(ff); if (this.getFontWeight(ff) !== '') font.weight = this.getFontWeight(ff); font.type = this.getFontType(ff, this.localUrl); + font.url = el.url; fonts.push(font); }); @@ -94,6 +95,7 @@ class FontsParser { if (this.getFontStyle(ff) !== '') font.style = this.getFontStyle(ff); if (this.getFontWeight(ff) !== '') font.weight = this.getFontWeight(ff); font.type = 'local'; + font.url = el.url; fonts.push(font); } }); @@ -110,6 +112,7 @@ class FontsParser { if (this.getFontStyle(ff) !== '') font.style = this.getFontStyle(ff); if (this.getFontWeight(ff) !== '') font.weight = this.getFontWeight(ff); font.type = 'local'; + font.url = el.url; fonts.push(font); } }); @@ -170,6 +173,7 @@ class FontsParser { if (this.getFontStyle(ff) !== '') font.style = this.getFontStyle(ff); if (this.getFontWeight(ff) !== '') font.weight = this.getFontWeight(ff); font.type = 'google'; + font.url = el.url; fonts.push(font); }); } @@ -198,6 +202,7 @@ class FontsParser { if (this.getFontStyle(ff) !== '') font.style = this.getFontStyle(ff); if (this.getFontWeight(ff) !== '') font.weight = this.getFontWeight(ff); font.type = 'typekit'; + font.url = el.url; fonts.push(font); }); } @@ -383,4 +388,4 @@ class FontsParser { } -module.exports = FontsParser; \ No newline at end of file +module.exports = FontsParser; diff --git a/lib/recommendation-collection.js b/lib/recommendation-collection.js new file mode 100644 index 0000000..ed8cd05 --- /dev/null +++ b/lib/recommendation-collection.js @@ -0,0 +1,39 @@ +/* + * recommendation-collection + * Copyright(c) 2018 Baptiste LARVOL-SIMON, http://www.e-glop.net/ + * MIT Licensed + */ + +'use strict' + +function RecommendationCollection(){ +} + +RecommendationCollection.prototype.collection = {}; + +/** + * @return void + **/ +RecommendationCollection.prototype.add = function(key, value) { + if ( !Array.isArray(this.collection[key]) ) { + this.collection[key] = [] + } + + this.collection[key].push(value); +} + +/** + * @return Array + **/ +RecommendationCollection.prototype.getTopics = function() { + return Object.keys(this.collection); +} + +/** + * @return Array + **/ +RecommendationCollection.prototype.getWarningsFor = function(key) { + return this.collection[key]; +} + +module.exports = RecommendationCollection; diff --git a/lib/tasks.js b/lib/tasks.js index f559a29..1c7a7e4 100644 --- a/lib/tasks.js +++ b/lib/tasks.js @@ -8,9 +8,11 @@ const HTMLParser = require('./html-parser'); const FontsParser = require('./fonts-parser'); // const tools = require('./tools'); const UI = require('./ui'); +const Cookies = require('./cookies'); +const RecommendationCollection = require('./recommendation-collection'); class Tasks { - constructor(url, uiInstance) { + constructor(url, uiInstance, args) { this.default_tasks = { normalize: { dependencies: [], @@ -29,6 +31,15 @@ class Tasks { dependencies: ['html', 'css', 'js'], mandatory: true }, + audit: { + dependencies: [], + }, + nfz: { + dependencies: [], + }, + cookies: { + dependencies: ['html', 'css'] + }, ssl: { dependencies: ['html', 'css'] }, @@ -57,9 +68,11 @@ class Tasks { this.ui = (!uiInstance) ? new UI() : uiInstance; this.hp; + this.args = args; this.url = url; this.tasks = []; this.data = {}; + this.recommendations = new RecommendationCollection(); // Put mandatory tasks already in the task list this.tasks = this.tasks.concat(this.getMandatoryTasks()); @@ -69,18 +82,20 @@ class Tasks { /** * Adds a new Task to the list * @param {string} task + * @param {string} arg value * @class Tasks */ - new(task) { + new(task, value) { this.default_tasks[task].dependencies.forEach(dep => { if (this.tasks.indexOf(dep) === -1) this.tasks.push(dep); }); if (this.tasks.indexOf(task) === -1) this.tasks.push(task); + if ( value !== undefined ) this.value = value; } /** - * Revoves a Task from the list + * Removes a Task from the list * @param {string} task * @class Tasks */ @@ -91,6 +106,18 @@ class Tasks { } + /** + * Removes a Task from the list and launch ending actions + * @param {string} task + * @class Tasks + */ + end(task) { + this.remove(task); + if ( this.tasks.length > 1 ) return; + this.getAuditInformations(); + } + + /** * Checks if the task list has a specifig task * @param {string} task @@ -153,12 +180,15 @@ class Tasks { // console.log('Remaining: ', this.tasks); this.getGeneralInformation(); + this.getNFZInformations(); this.getSSLInformation(); this.getFontInformation(); this.getSocialMediaInformation(); this.getPrefetchingInformation(); this.getAnalyticsInformation(); this.getCDNInformation(); + this.getCookiesInformation(); + this.getAuditInformations(); console.log(''); // console.log('Remaining: ', this.tasks); @@ -180,12 +210,18 @@ class Tasks { this.data.social.fb_graph = social.hasFacebookSocialGraph(this.data.js); this.data.social.pinterest = social.hasPinterest(this.data.js); - //console.log('FB CONNECT:', this.data.social.fb_connect); - //console.log('FB SOCIAL GRAPH:', this.data.social.fb_graph); - //console.log('PINTEREST:', this.data.social.pinterest); + this.ui.headline('Social Medias'); + + for ( var sm in this.data.social ) { + if ( !this.data.social[sm] ) continue; + this.recommendations.add('Social medias', sm); + } + if ( this.data.social.fb_connect ) this.ui.listitem('Facebook', 'Connect'); + if ( this.data.social.fb_graph ) this.ui.listitem('Facebook', 'Graph'); + if ( this.data.social.pinterest ) this.ui.listitem('Pinterest', ''); - this.remove('social'); + this.end('social'); } } @@ -217,10 +253,13 @@ class Tasks { this.ui.info(chalk.yellow(this.data.html.url.hostname) + ' uses ' + chalk.red(found.length) + ' Content Delivery Networks.\n'); found.forEach((cdn, i) => { this.ui.tableitem( - [chalk.yellow(String(i + 1) + '.'), cdn.host, chalk.dim(cdn.description)]); + [chalk.yellow(String(i + 1) + '.'), cdn.host, chalk.dim(cdn.description)] + ); }); + this.recommendations.add('Third Party', 'Content Delivery Networks'); } else this.ui.info('No Content Delivery Networks have been found.'); - this.remove('cdn'); + + this.end('cdn'); } } @@ -236,8 +275,11 @@ class Tasks { this.ui.headline('Analytics'); if (typeof this.data.analytics.ga === 'undefined' && typeof this.data.analytics.gtag === 'undefined' && - typeof this.data.analytics.piwik === 'undefined' && typeof this.data.analytics.wordpress === 'undefined') + typeof this.data.analytics.piwik === 'undefined' && typeof this.data.analytics.wordpress === 'undefined') { this.ui.info('No Analytics Tool has been found.'); + this.end('analytics'); + return; + } if (typeof this.data.analytics.ga !== 'undefined') { this.ui.info(chalk.red('Google Analytics') + ' has been found.\n'); @@ -264,7 +306,8 @@ class Tasks { ' (Jetpack) has been found.'); } - this.remove('analytics'); + this.recommendations.add('Third party', 'analytics - web statistics'); + this.end('analytics'); } } @@ -277,39 +320,42 @@ class Tasks { if (dns.length === 0) { this.ui.info( chalk.yellow(this.data.html.url.hostname) + - ' supports no DNS prefetching.') - } else { - this.ui.info( - chalk.yellow(this.data.html.url.hostname) + ' has ' + - chalk.red(dns.length) + ' DNS prefetching elements.\n'); - this.ui.settable({ - cells: 3, - widths: [5, 30, 0] - }); - - dns.forEach((url, i) => { - let u = (url.startsWith('//')) ? url.replace('//', '') : url; - let explanation = ''; - - if (u.indexOf('fonts.googleapis.com') >= 0) explanation = 'Google Fonts'; - if (u.indexOf('gravatar.com') >= 0) explanation = 'Automattic Gravatar Service'; - if (u.indexOf('s.w.org') >= 0) explanation = 'WordPress Emojis CDN'; - if (u.match(/s[0-9]\.wp\.com/)) explanation = 'WordPress Styles CDN'; - if (u.match(/i[0-9]\.wp\.com/)) explanation = 'WordPress Images CDN'; - if (u.match(/v[0-9]\.wordpress\.com/)) explanation = 'WordPress Videos CDN'; - if (u.indexOf('maxcdn.bootstrapcdn.com') >= 0) explanation = 'Bootstrap CDN'; - if (u.indexOf('checkout.stripe.com') >= 0) explanation = 'Stripe Online Payments'; - if (u.indexOf('code.jquery.com') >= 0) explanation = 'jQuery CDN'; - if (u.indexOf('translate.google.com') >= 0) explanation = 'Google Translate'; - if (u.indexOf('use.typekit.net') >= 0) explanation = 'Adobe Typekit Web Fonts'; - if (u.indexOf('use.fontawesome.com') >= 0) explanation = 'Font Awesome CDN'; - - this.ui.tableitem( - [chalk.yellow(String(i + 1) + '.'), u, chalk.dim(explanation)]) - }); + ' supports no DNS prefetching.'); + this.end('prefetching'); + return; } + + this.ui.info( + chalk.yellow(this.data.html.url.hostname) + ' has ' + + chalk.red(dns.length) + ' DNS prefetching elements.\n'); + this.ui.settable({ + cells: 3, + widths: [5, 30, 0] + }); - this.remove('prefetching'); + dns.forEach((url, i) => { + let u = (url.startsWith('//')) ? url.replace('//', '') : url; + let explanation = ''; + + if (u.indexOf('fonts.googleapis.com') >= 0) explanation = 'Google Fonts'; + if (u.indexOf('gravatar.com') >= 0) explanation = 'Automattic Gravatar Service'; + if (u.indexOf('s.w.org') >= 0) explanation = 'WordPress Emojis CDN'; + if (u.match(/s[0-9]\.wp\.com/)) explanation = 'WordPress Styles CDN'; + if (u.match(/i[0-9]\.wp\.com/)) explanation = 'WordPress Images CDN'; + if (u.match(/v[0-9]\.wordpress\.com/)) explanation = 'WordPress Videos CDN'; + if (u.indexOf('maxcdn.bootstrapcdn.com') >= 0) explanation = 'Bootstrap CDN'; + if (u.indexOf('checkout.stripe.com') >= 0) explanation = 'Stripe Online Payments'; + if (u.indexOf('code.jquery.com') >= 0) explanation = 'jQuery CDN'; + if (u.indexOf('translate.google.com') >= 0) explanation = 'Google Translate'; + if (u.indexOf('use.typekit.net') >= 0) explanation = 'Adobe Typekit Web Fonts'; + if (u.indexOf('use.fontawesome.com') >= 0) explanation = 'Font Awesome CDN'; + + this.ui.tableitem( + [chalk.yellow(String(i + 1) + '.'), u, chalk.dim(explanation)]) + }); + this.recommendations.add('Third party', 'DNS Prefetching'); + + this.end('prefetching'); } } @@ -335,7 +381,6 @@ class Tasks { this.data.fonts = {}; const Fonts = new FontsParser(this.data.css, this.data.js, this.data.html.url); this.data.fonts = Fonts.getFonts(); - //console.log(this.data.fonts); let todo = []; default_types.forEach(el => { @@ -349,6 +394,7 @@ class Tasks { }); if (todo.length !== 0) { + var reco = false; todo.forEach((el, i) => { let count = Fonts.countFonts(this.data.fonts[el.type]); if (i > 0) console.log(''); @@ -356,10 +402,12 @@ class Tasks { count.forEach((f, j) => { this.ui.tableitem([chalk.yellow(String(j + 1) + '.'), f, chalk.dim(Fonts.getFontStyles(this.data.fonts[el.type], f))]); }); + if ( el.type != 'local' ) reco = true; }); + if ( reco ) this.recommendations.add('Third party', 'fonts'); } else this.ui.info('There were no Fonts found.'); - this.remove('fonts'); + this.end('fonts'); } } @@ -373,7 +421,8 @@ class Tasks { this.data.ssl.constructor === Object) { this.ui.headline('SSL Certificate'); this.ui.error('There is no SSL/TLS available.', false); - this.remove('ssl'); + this.recommendations.add('Security', 'available SSL certificate (it is mandatory that data is transmitted loud & clear)'); + this.end('ssl'); return; } else { this.ui.headline('SSL Certificate'); @@ -389,12 +438,194 @@ class Tasks { this.ui.listitem('FP SHA-1', this.data.ssl.fingerprint); this.ui.listitem('FP SHA-256', this.data.ssl.fingerprint256); //this.ui.message('SSL FINISHED'); - this.remove('ssl'); + this.end('ssl'); } } } + /** + * Gathers Cookies Information + * @class Tasks + */ + getCookiesInformation() { + if (!this.hasTask('cookies')) { + return; + } + + var raw_cookies = [], + waitfor = []; + + this.args.cookies = parseInt(this.args.cookies,10) ? parseInt(this.args.cookies,10) : 13; + + // direct cookies + if (typeof this.data.html.headers['set-cookie'] == 'object') { + raw_cookies.push(this.data.html.headers['set-cookie']); + } + + // process JS & CSS + [this.data.js, this.data.css].forEach(type => { + type.forEach(data => { + if (typeof data.headers['set-cookie'] == 'object') { + raw_cookies.push(data.headers['set-cookie']); + } + }); + }); + + // process analytics + if ( this.data.analytics ) { + for ( var i in this.data.analytics ) { + waitfor.push('analytics'); + this.loadExternalItem(this.data.analytics[i]).then(res => { + if (typeof res.headers['set-cookie'] == 'object') { + raw_cookies.push(res.headers['set-cookie']); + } + waitfor.splice(waitfor.indexOf('analytics'), 1); + this._innerCookies(raw_cookies, waitfor); + }); + } + } + + // process fonts + if ( this.data.fonts ) { + for ( var i in this.data.fonts ) { + if ( i == 'local' ) continue; + waitfor.push('fonts'); + this.data.fonts[i].forEach(font => { + this.loadExternalItem(font).then(res => { + if (typeof res.headers['set-cookie'] == 'object') { + raw_cookies.push(res.headers['set-cookie']); + } + waitfor.splice(waitfor.indexOf('fonts'), 1); + this._innerCookies(raw_cookies, waitfor); + }); + }); + } + } + + this._innerCookies(raw_cookies, waitfor); + } + + _innerCookies(raw_cookies, waitfor) { + // go away if still waiting for async calls + if ( waitfor.length > 0 ) return; + + // go away if no cookie is given + if ( raw_cookies.length == 0 ) return; + + var getter = new Cookies(raw_cookies); + var cookies = getter.fetchAll(); + + if ( cookies.length == 0 ) { + this.ui.headline('No cookie set'); + this.end('cookies'); + return; + } + + var strtotime = require('locutus/php/datetime/strtotime'); + + this.ui.headline('Cookies'); + var reco = false; + + for ( var i in cookies ) { + if ( cookies[i].expires == undefined ) { + this.ui.listitem(cookies[i].name, 'Expires at the end of the user session and '+(cookies[i].secure ? 'secure' : 'unsecure')); + continue; + } + + var t1 = cookies[i].expires.getTime(), + t2 = new Date(strtotime('+'+this.args.cookies+' month 1 hour')*1000).getTime(); + + if ( t1 > t2 ) { + this.ui.listitem(chalk.red(cookies[i].name), 'expires in more than '+this.args.cookies+' month (expires on '+cookies[i].expires.toLocaleDateString()+')', false); + reco = true; + continue; + } + + this.ui.listitem(cookies[i].name, 'Expires on '+cookies[i].expires.toLocaleDateString()+' and '+(cookies[i].secure ? 'secure' : 'unsecure')); + } + + if ( reco ) this.recommendations.add('Cookies', 'expiration delay'); + + this.end('cookies'); + return; + } + + /** + * Gathes further recommendations for further human audit + * @class Tasks + */ + getAuditInformations() { + if (!this.hasTask('audit')) return; + + this.ui.headline('Recommendations for further human audit'); + + var topics = this.recommendations.getTopics(); + for ( var i in topics ) { + var recos = this.recommendations.getWarningsFor(topics[i]); + recos.forEach(reco => { + this.ui.listitem(topics[i], 'Check '+reco); + }); + } + } + + /** + * Gathes NF Z67-147 informations (meta data) about the audit + * @class Tasks + */ + getNFZInformations() { + if (!this.hasTask('nfz')) return; + + const dns = require('dns'); + const tld = this.url.replace(/^https{0,1}:\/\/([^\/]+)\/.*$/, '$1'); + var tasks = this; + + var w4, w6, a4, a6; + + dns.resolve(tld, 'A', function(err, addresses){ + w4 = err !== null ? null : addresses; + tasks._innerNFZInformations(w4, w6, a4, a6); + }); + dns.resolve(tld, 'AAAA', function(err, addresses){ + w6 = err !== null ? null : addresses; + tasks._innerNFZInformations(w4, w6, a4, a6); + }); + dns.resolve('resolver1.opendns.com', 'A', function(err, addresses){ + dns.setServers(addresses); + dns.resolve('myip.opendns.com', 'A', function(err, addresses){ + a4 = err !== null ? null : addresses; + tasks._innerNFZInformations(w4, w6, a4, a6); + }); + dns.resolve('myip.opendns.com', 'AAAA', function(err, addresses){ + a6 = err !== null ? null : addresses; + tasks._innerNFZInformations(w4, w6, a4, a6); + }); + }); + } + + _innerNFZInformations(w4, w6, a4, a6) { + if ( w4 === undefined || w6 === undefined || a4 === undefined || a6 === undefined ) return; + + const os = require('os') + const pkg = require('../package.json'); + + this.ui.headline('NF Z67-147 informations about the current audit'); + + this.ui.listitem('Operating system', os.type()+' '+os.release()+' '+os.arch()); + this.ui.listitem('Software', pkg.name+'-'+pkg.version+' - '+pkg.description); + this.ui.listitem('User Agent', this.default_headers['user-agent']); + this.ui.listitem('Web cache', 'Empty'); + this.ui.listitem('Web cookies', 'Empty'); + this.ui.listitem('Web proxy', 'Null'); + this.ui.listitem('Viruses', os.type() == 'Linux' ? 'Unix system up-to-date and not corrupted' : 'Unverified'); + this.ui.listitem('Date & time', new Date().toLocaleString()); + + if ( a4 !== null ) this.ui.listitem("Auditor's IPv4", a4); + if ( a6 !== null ) this.ui.listitem("Auditor's IPv6", a6); + if ( w4 !== null ) this.ui.listitem("Website IPv4", w4); + if ( w6 !== null ) this.ui.listitem("Website IPv6", w6); + } + /** * Gathes General Information (meta data) of the Website * @class Tasks @@ -418,7 +649,7 @@ class Tasks { if (typeof this.data.general.plugins !== 'undefined' && this.data.general.plugins.length > 0) this.ui.listitem('Plugins', this.data.general.plugins.join(', ')); } - this.remove('general'); + this.end('general'); } } @@ -450,7 +681,7 @@ class Tasks { // console.log(this.data.js); } // this.ui.message('ADDITIONAL CONTENT FINISHED'); - this.remove('html'); + this.end('html'); return Promise.resolve(this.urls); } else return Promise.resolve(100); // Status 100 CONTINUE @@ -519,7 +750,7 @@ class Tasks { }, false); this.data.js.inline = this.hp.getInlineJS(); // this.ui.message('JS FINISHED'); - this.remove('js'); + this.end('js'); return res; }); } else @@ -548,7 +779,7 @@ class Tasks { }, false); this.data.css.inline = this.hp.getInlineCSS(); // this.ui.message('CSS FINISHED'); - this.remove('css'); + this.end('css'); return res; }); } else @@ -615,9 +846,19 @@ class Tasks { * @param {URL} item */ loadExternalItem(item) { + var tasks = this; + return new Promise(resolve => { const Obj = {}; Obj.url = item.url; + + // manage '^//' URLs adding the default protocol as a prefix + if ( typeof item.url == 'string' && + item.url.replace(/^(\/\/).*$/, '$1') == '//' && + tasks.data.html.url !== undefined ) { + item.url = tasks.data.html.url.protocol + item.url; + } + got(item.url).then( res => { Obj.headers = res.headers; @@ -664,10 +905,11 @@ class Tasks { chalk.yellow(this.data.html.url.href) + '.' }); } - this.remove('normalize'); + this.end('normalize'); Promise.resolve(200); }); } } -module.exports = Tasks; \ No newline at end of file +module.exports = Tasks; + diff --git a/package.json b/package.json index 85d1514..af0b1dd 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ }, "repository": "https://github.com/mirkoschubert/gdpr-check.git", "author": "Mirko Schubert ", + "contributors": ["Baptiste LARVOL-SIMON (http://www.e-glop.net/)"], "license": "MIT", "dependencies": { "chalk": "^2.4.1", @@ -20,13 +21,22 @@ "cliui": "^4.1.0", "commander": "^2.15.1", "css": "^2.2.3", + "depd": "~1.1.2", "fs-extra": "^6.0.1", "get-ssl-certificate": "^2.1.2", "got": "^8.3.1", + "keygrip": "~1.0.3", + "locutus": "^2.0.10", "moment": "^2.22.1", "rootpath": "^0.1.2" }, "devDependencies": { - "ava": "^0.25.0" + "ava": "^0.25.0", + "eslint": "3.19.0", + "express": "4.16.4", + "istanbul": "0.4.5", + "mocha": "5.2.0", + "restify": "6.4.0", + "supertest": "3.3.0" } -} \ No newline at end of file +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..29e4b34 --- /dev/null +++ b/test.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node --harmony + +'use strict' + +const arr = []; +console.log(typeof arr); + +/* +const os = require('os'); +const pkg = require('./package.json'); + +console.error(os.type(), os.release(), os.arch()); +console.error(pkg.name, pkg.version, pkg.description); +console.error(new Date().toLocaleString()); + +const dns = require('dns'); +const url = 'http://myurl.tld/glop'; +console.error(url.replace(/^https{0,1}:\/\/([^\/]+)\/.*$/, '$1')); +//dns.resolve('resolver1.opendns.com', 'A', function(err, addresses){ + +/* +dns.resolve('resolver1.opendns.com', 'A', function(err, addresses){ + console.error(err, addresses); + dns.setServers(addresses); + dns.resolve('myip.opendns.com', 'A', function(err, addresses){ + console.error(err, addresses); + }); +}) +*/