Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ 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('-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]')
Expand All @@ -25,6 +26,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
Expand All @@ -46,9 +48,11 @@ 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.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');
Expand All @@ -72,4 +76,4 @@ app

app.parse(process.argv);

//if (!app.args.length) app.help();
//if (!app.args.length) app.help();
128 changes: 128 additions & 0 deletions lib/cookies.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*!
* 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

function Cookies(response){
this.response = response;
}

/**
* @return Array
**/
Cookies.prototype.fetchAll = function() {
var header, cookies = [];

header = this.response.headers["set-cookie"];
if (!header) return [];

header.forEach(cook => {
var name,
attrs = [],
data = cook.split(/\\{0}; /)

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
124 changes: 121 additions & 3 deletions lib/tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ const HTMLParser = require('./html-parser');
const FontsParser = require('./fonts-parser');
// const tools = require('./tools');
const UI = require('./ui');
const Cookies = require('./cookies');

class Tasks {
constructor(url, uiInstance) {
constructor(url, uiInstance, args) {
this.default_tasks = {
normalize: {
dependencies: [],
Expand All @@ -29,6 +30,12 @@ class Tasks {
dependencies: ['html', 'css', 'js'],
mandatory: true
},
nfz: {
dependencies: [],
},
cookies: {
dependencies: ['html', 'css']
},
ssl: {
dependencies: ['html', 'css']
},
Expand Down Expand Up @@ -57,6 +64,7 @@ class Tasks {
this.ui = (!uiInstance) ? new UI() : uiInstance;
this.hp;

this.args = args;
this.url = url;
this.tasks = [];
this.data = {};
Expand All @@ -69,13 +77,15 @@ 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;
}


Expand Down Expand Up @@ -153,12 +163,14 @@ 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();

console.log('');
// console.log('Remaining: ', this.tasks);
Expand All @@ -180,6 +192,10 @@ class Tasks {
this.data.social.fb_graph = social.hasFacebookSocialGraph(this.data.js);
this.data.social.pinterest = social.hasPinterest(this.data.js);

for ( var sm in this.data.social ) {
if ( !this.data.social[sm] ) continue;
}

//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);
Expand Down Expand Up @@ -395,6 +411,107 @@ class Tasks {
}


/**
* Gathers Cookies Information
* @class Tasks
*/
getCookiesInformation() {
if (!this.hasTask('cookies')) {
return;
}

this.args.cookies = parseInt(this.args.cookies,10) ? parseInt(this.args.cookies,10) : 13;

// go away if no cookie is given
if (typeof this.data.html.headers['set-cookie'] != 'object') {
return;
}

var getter = new Cookies(this.data.html);
var cookies = getter.fetchAll();

if ( cookies.length == 0 ) {
this.ui.headline('No cookie set');
this.remove('cookies');
return;
}

var strtotime = require('locutus/php/datetime/strtotime');

this.ui.headline('Cookies');
for ( var i in cookies ) {
var t1 = cookies[i].expires.getTime(),
t2 = new Date(strtotime('+'+this.args.cookies+' month 1 hour')*1000).getTime();

if ( t1 > t2 ) {
this.ui.error('Cookie "'+cookies[i].name+'" expires in more than '+this.args.cookies+' month (expires on '+cookies[i].expires.toLocaleDateString()+')', true);
continue;
}
this.ui.listitem(cookies[i].name, 'Expires on '+cookies[i].expires.toLocaleDateString()+' and '+(cookies[i].secure ? 'secure' : 'unsecure'));
}

this.remove('cookies');
return;
}


/**
* 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
Expand Down Expand Up @@ -670,4 +787,5 @@ class Tasks {
}
}

module.exports = Tasks;
module.exports = Tasks;

14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,30 @@
},
"repository": "https://github.com/mirkoschubert/gdpr-check.git",
"author": "Mirko Schubert <mirkoschubert@gmail.com>",
"contributors": ["Baptiste LARVOL-SIMON (http://www.e-glop.net/)"],
"license": "MIT",
"dependencies": {
"chalk": "^2.4.1",
"cheerio": "^1.0.0-rc.2",
"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"
}
}
}
Loading