Security improvement batch (#6622)

* Adds a new method for the server to push notifies to the client, which require administration privileges from the user. If there are messages in queue but user is not privileged, she is notified of pending messages

* Fix unit tests

* Increase timeouts on tests

* Add translations

* * Aggregate admin messages
* Send admin message on auth fail
* Sending messages over bus
* XSS filtering of objects sent over the REST API

* Warn users if their instance is world readable

* Fix adminnotifies init()

* Fix couple issues from Codacy
This commit is contained in:
Sulka Haro
2021-01-07 22:46:55 +02:00
committed by GitHub
parent df6d9aadc3
commit 914ba78f36
34 changed files with 12059 additions and 117 deletions
-1
View File
@@ -28,5 +28,4 @@ npm-debug.log
/cgm-remote-monitor.njsproj
/cgm-remote-monitor.sln
/obj/Debug
/bin
/*.bat
File diff suppressed because one or more lines are too long
+117
View File
@@ -0,0 +1,117 @@
'use strict';
const axios = require('axios');
const moment = require('moment');
const crypto = require('crypto');
const shasum = crypto.createHash('sha1');
const FIVE_MINUTES = 1000 * 60 * 5;
if (process.argv.length < 4) {
console.error('This is an utility to send continuous CGM entry data to a test Nightscout server')
console.error('USAGE: node testdatarunner.js <SERVER BASE URL> <API_SECRET>');
process.exit();
}
const URL = process.argv[2];
const SECRET = process.argv[3];
shasum.update(SECRET);
const SECRET_SHA1 = shasum.digest('hex');
const HEADERS = {'api-secret': SECRET_SHA1};
const ENTRIES_URL = URL + '/api/v1/entries';
var done = (function wait () { if (!done) setTimeout(wait, 1000); })();
console.log('NS data filler active');
const entry = {
device: 'Dev simulator',
date: 1609061083612,
dateString: '2020-12-27T09:24:43.612Z',
sgv: 100,
delta: 0,
direction: 'Flat',
type: 'sgv'
};
function addEntry () {
console.log('Sending add new entry');
sendEntry(Date.now());
setTimeout(addEntry, FIVE_MINUTES);
}
function oscillator(time, frequency = 1, amplitude = 1, phase = 0, offset = 0){
return Math.sin(time * frequency * Math.PI * 2 + phase * Math.PI * 2) * amplitude + offset;
}
async function sendFail() {
try {
console.log('Sending fail');
const response = await axios.post(ENTRIES_URL, entry, {headers: {'api-secret': 'incorrect' }});
} catch (e) { }
}
async function sendEntry (date) {
const m = moment(date);
entry.date = date;
entry.dateString = m.toISOString();
entry.sgv = 100 + Math.round(oscillator(date / 1000, 1/(60*60), 30));
console.log('Adding entry', entry);
const response = await axios.post(ENTRIES_URL, entry, {headers: HEADERS});
if (date > Date.now() - 5000) sendFail();
}
(async () => {
try {
console.log('GETTING', ENTRIES_URL);
const response = await axios.get(ENTRIES_URL, {headers: HEADERS} );
const latestEntry = response.data ? response.data[0] : null;
if (!latestEntry) {
// Fill in history
console.log('I would fill in history');
const totalToSave = 24;
const now = Date.now();
const start = now - ( totalToSave * FIVE_MINUTES);
let current = start;
while (current <= now) {
await sendEntry(current);
current += FIVE_MINUTES;
}
setTimeout(addEntry, 1000*60*5);
} else {
let latestDate = latestEntry.date;
const now = Date.now();
if ((now - latestDate) > FIVE_MINUTES) {
console.log('We got data but it is older than 5 minutes, makign a partial fill');
let current = latestDate + FIVE_MINUTES;
while (current < now) {
await sendEntry(current);
current += FIVE_MINUTES;
}
latestDate = current;
} else {
console.log('Looks like we got history, not filling');
}
setTimeout(addEntry, Date.now() - latestDate);
}
sendFail();
sendFail();
} catch (error) {
console.log(error.response.data);
}
})();
+26 -13
View File
@@ -1,16 +1,19 @@
'use strict';
var _each = require('lodash/each');
var _trim = require('lodash/trim');
var _forIn = require('lodash/forIn');
var _startsWith = require('lodash/startsWith');
var _camelCase = require('lodash/camelCase');
const _each = require('lodash/each');
const _trim = require('lodash/trim');
const _forIn = require('lodash/forIn');
const _startsWith = require('lodash/startsWith');
const _camelCase = require('lodash/camelCase');
var fs = require('fs');
var crypto = require('crypto');
var consts = require('./lib/constants');
const owasp = require('owasp-password-strength-test');
var env = {
const fs = require('fs');
const crypto = require('crypto');
const consts = require('./lib/constants');
const env = {
settings: require('./lib/settings')()
};
@@ -36,9 +39,8 @@ function config ( ) {
minify: readENVTruthy('DEBUG_MINIFY', true)
};
if (env.err) {
delete env.err;
}
env.err = [];
env.notifies = [];
setSSL();
setAPISecret();
@@ -81,10 +83,21 @@ function setAPISecret() {
if (readENV('API_SECRET').length < consts.MIN_PASSPHRASE_LENGTH) {
var msg = ['API_SECRET should be at least', consts.MIN_PASSPHRASE_LENGTH, 'characters'].join(' ');
console.error(msg);
env.err = {desc: msg};
env.err.push({ desc: msg });
} else {
var shasum = crypto.createHash('sha1');
shasum.update(readENV('API_SECRET'));
var testresult = owasp.test(readENV('API_SECRET'));
const messages = testresult.errors;
if (messages) {
messages.forEach(message => {
const m = message.replace('The password must', 'API_SECRET should');
env.notifies.push({persistent: true, title: 'Security issue', message: m + ' Please change your API_SECRET to reduce risk of unauthorized access.'});
});
}
env.api_secret = shasum.digest('hex');
}
}
+52
View File
@@ -0,0 +1,52 @@
'use strict';
const _ = require('lodash');
function init (ctx) {
const adminnotifies = {};
adminnotifies.addNotify = function addnotify (notify) {
if (!notify) return;
notify.title = notify.title || 'No title';
notify.message = notify.message || 'No message';
const existingMessage = _.find(adminnotifies.notifies, function findExisting (obj) {
return obj.message == notify.message;
});
if (existingMessage) {
existingMessage.count += 1;
existingMessage.lastRecorded = Date.now();
} else {
notify.count = 1;
notify.lastRecorded = Date.now();
adminnotifies.notifies.push(notify);
}
}
adminnotifies.getNotifies = function getNotifies () {
return adminnotifies.notifies;
}
ctx.bus.on('admin-notify', adminnotifies.addNotify);
adminnotifies.clean = function cleanNotifies () {
adminnotifies.notifies = _.filter(adminnotifies.notifies, function findExisting (obj) {
return obj.persistent || ((Date.now() - obj.lastRecorded) < 1000 * 60 * 60 * 12);
});
}
adminnotifies.cleanAll = function cleanAll() {
adminnotifies.notifies = [];
}
adminnotifies.cleanAll();
ctx.bus.on('tick', adminnotifies.clean);
return adminnotifies;
}
module.exports = init;
+35
View File
@@ -0,0 +1,35 @@
'use strict';
const _ = require('lodash');
const consts = require('../constants');
function configure (ctx) {
const express = require('express')
, api = express.Router();
api.get('/adminnotifies', function(req, res) {
ctx.authorization.resolveWithRequest(req, function resolved (err, result) {
const isAdmin = ctx.authorization.checkMultiple('*:*:admin', result.shiros); //full admin permissions
const response = {
notifies: []
, notifyCount: 0
};
if (ctx.adminnotifies) {
const notifies = _.filter(ctx.adminnotifies.getNotifies(), function isOld (obj) {
return (obj.persistent || (Date.now() - obj.lastRecorded) < 1000 * 60 * 60 * 8);
});
if (isAdmin) { response.notifies = notifies }
response.notifyCount = notifies.length;
}
res.sendJSONStatus(res, consts.HTTP_OK, response);
});
});
return api;
}
module.exports = configure;
+3
View File
@@ -67,6 +67,9 @@ function configure (app, wares, ctx, env) {
function doPost (req, res) {
var obj = req.body;
ctx.purifier.purifyObject(obj);
ctx.devicestatus.create(obj, function(err, created) {
if (err) {
res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
+5
View File
@@ -274,6 +274,11 @@ function configure (app, wares, ctx, env) {
incoming = incoming.concat(req.body);
}
for (let i = 0; i < incoming.length; i++) {
const e = incoming[i];
ctx.purifier.purifyObject(e);
}
/**
* @function persist
* @returns {WritableStream} a writable persistent storage stream
+3
View File
@@ -58,6 +58,9 @@ function create (env, ctx) {
app.all('/activity*', require('./activity/')(app, wares, ctx));
app.use('/', wares.sendJSONStatus, require('./verifyauth')(ctx));
app.use('/', wares.sendJSONStatus, require('./adminnotifiesapi')(ctx));
app.all('/food*', require('./food/')(app, wares, ctx));
// Status first
+1
View File
@@ -61,6 +61,7 @@ function configure (app, wares, ctx) {
// create new record
api.post('/profile/', ctx.authorization.isPermitted('api:profile:create'), function(req, res) {
var data = req.body;
ctx.purifier.purifyObject(data);
ctx.profile.create(data, function (err, created) {
if (err) {
res.sendJSONStatus(res, consts.HTTP_INTERNAL_ERROR, 'Mongo Error', err);
+2
View File
@@ -119,6 +119,8 @@ function configure (app, wares, ctx, env) {
t.created_at = new Date().toISOString();
}
ctx.purifier.purifyObject(t);
/*
if (!t.created_at) {
console.log('Trying to create treatment without created_at field', t);
-2
View File
@@ -9,8 +9,6 @@ function init (env) {
const DELAY_ON_FAIL = _.get(env, 'settings.authFailDelay') || 5000;
const FAIL_AGE = 60000;
const sleep = require('util').promisify(setTimeout);
ipDelayList.addFailedRequest = function addFailedRequest (ip) {
const ipString = String(ip);
let entry = ipDelayList[ipString];
+7 -3
View File
@@ -5,10 +5,8 @@ const jwt = require('jsonwebtoken');
const shiroTrie = require('shiro-trie');
const consts = require('./../constants');
const sleep = require('util').promisify(setTimeout);
function getRemoteIP (req) {
return req.headers['x-forwarded-for'] || req.connection.remoteAddress;
}
@@ -208,7 +206,13 @@ function init (env, ctx) {
console.error('Resolving secret/token to permissions failed');
addFailedRequest(data.ip);
if (callback) { callback('All validation failed', {}); }
ctx.bus.emit('admin-notify', {
title: ctx.language.translate('Failed authentication')
, message: ctx.language.translate('A device at IP number') + ' ' + data.ip + ' ' + ctx.language.translate('attempted authenticating with Nightscout with wrong credentials. Check if you have an uploader setup with wrong API_SECRET or token?')
});
if (callback) { callback('All validation failed', {}); }
return {};
};
+100
View File
@@ -0,0 +1,100 @@
'use strict';
function init (client, $) {
var notifies = {};
client.notifies = notifies;
notifies.notifies = [];
notifies.drawer = $('#adminNotifiesDrawer');
notifies.button = $('#adminnotifies');
notifies.updateAdminNotifies = function updateAdminNotifies() {
var src = '/api/v1/adminnotifies?t=' + new Date().getTime();
$.ajax({
method: 'GET'
, url: src
, headers: client.headers()
}).done(function success (results) {
if (results.message) {
var m = results.message;
client.notifies.notifies = m.notifies;
client.notifies.notifyCount = m.notifyCount;
if (m.notifyCount > 0) {
notifies.button.show();
}
}
window.setTimeout(notifies.updateAdminNotifies, 1000*60);
}).fail(function fail () {
console.error('Failed to load notifies');
window.setTimeout(notifies.updateAdminNotifies, 1000*60);
});
}
notifies.updateAdminNotifies();
function wrapmessage(title, message, count, ago, persistent) {
let html = '<hr><p><b>' + title + '</b></p><p class="adminNotifyMessage">' + message + '</p>';
let additional = '';
if (count > 1) additional += 'Event repeated ' + count + ' times.' + ' ';
let units = 'minutes';
if (ago > 60) {
ago = ago / 60;
units = 'hours';
}
if (!persistent) additional += 'Last recorded ' + ago + ' '+ units + ' ago.';
if (additional) html += '<p class="adminNotifyMessageAdditionalInfo">' + additional + '</p>'
return html;
}
notifies.prepare = function prepare() {
var translate = client.translate;
var html = '<div id="adminNotifyContent">';
var messages = client.notifies.notifies;
var messageCount = client.notifies.notifyCount;
if (messages && messages.length > 0) {
html += '<p><b>' + translate('You have administration messages') + '</b></p>';
for(var i = 0 ; i < messages.length; i++) {
var m = messages[i];
const ago = Math.round((Date.now() - m.lastRecorded) / 60000);
html += wrapmessage(m.title, m.message, m.count, ago, m.persistent);
}
} else {
if (messageCount > 0) {
html = wrapmessage(translate('Admin messages in queue'), translate('Please sign in using the API_SECRET to see your administration messages'));
} else {
html = wrapmessage(translate('Queue empty'), translate('There are no admin messages in queue'));
}
}
html += '<hr></div>';
notifies.drawer.html(html);
}
function maybePrevent (event) {
if (event) {
event.preventDefault();
}
}
notifies.toggleDrawer = function toggleDrawer (event) {
client.browserUtils.toggleDrawer('#adminNotifiesDrawer', notifies.prepare);
maybePrevent(event);
};
notifies.button.click(notifies.toggleDrawer);
notifies.button.css('color','red');
return notifies;
}
module.exports = init;
+4
View File
@@ -237,6 +237,8 @@ client.load = function load (serverSettings, callback) {
//After plugins are initialized with browser settings;
browserSettings.loadAndWireForm();
client.adminnotifies = require('./adminnotifiesclient')(client, $);
if (serverSettings && serverSettings.authorized) {
client.authorized = serverSettings.authorized;
client.authorized.lat = Date.now();
@@ -266,6 +268,8 @@ client.load = function load (serverSettings, callback) {
$('#treatmentDrawerToggle').toggle(treatmentCreateAllowed && client.settings.showPlugins.indexOf('careportal') > -1);
$('#boluscalcDrawerToggle').toggle(treatmentCreateAllowed && client.settings.showPlugins.indexOf('boluscalc') > -1);
if (isAuthenticated) client.notifies.updateAdminNotifies();
// Edit mode
editButton.toggle(client.settings.editMode && treatmentUpdateAllowed);
editButton.click(function editModeClick (event) {
+20 -16
View File
@@ -1,8 +1,7 @@
'use strict';
var _ = require('lodash');
var UPDATE_THROTTLE = 5000;
const _ = require('lodash');
const UPDATE_THROTTLE = 5000;
function boot (env, language) {
@@ -11,23 +10,19 @@ function boot (env, language) {
console.log('Executing startBoot');
ctx.runtimeState = 'booting';
ctx.bus = require('../bus')(env.settings, ctx);
ctx.adminnotifies = require('../adminnotifies')(ctx);
if (env.notifies) {
ctx.adminnotifies.addNotify(env.notifies[0]); // TODO iterate all
}
next();
}
//////////////////////////////////////////////////
// Check Node version.
// Latest Node 8 LTS and Latest Node 10 LTS are recommended and supported.
// Latest Node version on Azure is tolerated, but not recommended
// Latest Node (non LTS) version works, but is not recommended
// Latest Node 10 to 14 LTS are recommended and supported.
// Older Node versions or Node versions with known security issues will not work.
// More explicit:
// < 8 does not work, not supported
// >= 8.15.1 works, supported and recommended
// == 9.x does not work, not supported
// == 10.15.2 works, not fully supported and not recommended (Azure version)
// >= 10.16.0 works, supported and recommended
// == 11.x does not work, not supported
// >= 12.6.0 does work, not recommended, will not be supported. We only support Node LTS releases
///////////////////////////////////////////////////
function checkNodeVersion (ctx, next) {
@@ -56,7 +51,7 @@ function boot (env, language) {
console.log('Executing checkEnv');
ctx.language = language;
if (env.err) {
if (env.err.length > 0) {
ctx.bootErrors = ctx.bootErrors || [ ];
ctx.bootErrors.push({'desc': 'ENV Error', err: env.err});
}
@@ -121,6 +116,15 @@ function boot (env, language) {
err: 'API_SECRET setting is missing, cannot enable REST API'});
}
if (env.settings.authDefaultRoles == 'readable') {
const message = {
title: "Nightscout readable by world"
,message: "Your Nightscout installation is readable by anyone who knows the web page URL. Please consider closing access to the site by following the instructions in the <a href=\"http://nightscout.github.io/nightscout/admin_tools/#turn-off-unauthorized-access\" target=\"_new\">Nightscout documentation</a>."
,persistent: true
};
ctx.adminnotifies.addNotify(message);
}
next();
}
@@ -215,11 +219,11 @@ function boot (env, language) {
ctx.food = require('./food')(env, ctx);
ctx.pebble = require('./pebble')(env, ctx);
ctx.properties = require('../api/properties')(env, ctx);
ctx.bus = require('../bus')(env.settings, ctx);
ctx.ddata = require('../data/ddata')();
ctx.cache = require('./cache')(env,ctx);
ctx.dataloader = require('../data/dataloader')(env, ctx);
ctx.notifications = require('../notifications')(env, ctx);
ctx.purifier = require('./purifier')(env,ctx);
if (env.settings.isEnabled('alexa') || env.settings.isEnabled('googlehome')) {
ctx.virtAsstBase = require('../plugins/virtAsstBase')(env, ctx);
+36
View File
@@ -0,0 +1,36 @@
'use strict';
const createDOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const window = new JSDOM('').window;
const DOMPurify = createDOMPurify(window);
function init (env, ctx) {
const purifier = {};
function iterate (obj) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == 'object')
iterate(obj[property]);
else
if (isNaN(obj[property])) {
const clean = DOMPurify.sanitize(obj[property]);
if (obj[property] !== clean) {
obj[property] = clean;
}
}
}
}
}
purifier.purifyObject = function purifyObject (obj) {
return iterate(obj);
}
return purifier;
}
module.exports = init;
+86 -61
View File
@@ -1217,9 +1217,9 @@
},
"dependencies": {
"acorn": {
"version": "6.4.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
"integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA=="
"version": "6.4.2",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.2.tgz",
"integrity": "sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ=="
}
}
},
@@ -1551,6 +1551,15 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
},
"axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"dev": true,
"requires": {
"follow-redirects": "^1.10.0"
}
},
"babel-code-frame": {
"version": "6.26.0",
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
@@ -2992,9 +3001,9 @@
},
"dependencies": {
"abab": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
"integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ=="
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.5.tgz",
"integrity": "sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q=="
},
"whatwg-url": {
"version": "7.1.0",
@@ -3171,6 +3180,11 @@
"webidl-conversions": "^4.0.2"
}
},
"dompurify": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz",
"integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ=="
},
"dot-prop": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz",
@@ -3566,6 +3580,18 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
},
"escodegen": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
"requires": {
"esprima": "^4.0.1",
"estraverse": "^4.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1",
"source-map": "~0.6.1"
}
},
"eslint": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz",
@@ -3761,6 +3787,11 @@
}
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"esquery": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
@@ -3793,6 +3824,11 @@
}
}
},
"estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
},
"esutils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -4075,6 +4111,11 @@
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"fastparse": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
@@ -4253,6 +4294,12 @@
"readable-stream": "^2.3.6"
}
},
"follow-redirects": {
"version": "1.13.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.1.tgz",
"integrity": "sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg==",
"dev": true
},
"for-in": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
@@ -5580,65 +5627,10 @@
"xml-name-validator": "^3.0.0"
},
"dependencies": {
"escodegen": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
"requires": {
"esprima": "^4.0.1",
"estraverse": "^4.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1",
"source-map": "~0.6.1"
}
},
"esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
"integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="
},
"estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw=="
},
"fast-levenshtein": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
}
},
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
}
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"optional": true
}
}
},
@@ -5792,6 +5784,15 @@
"leven": "^3.1.0"
}
},
"levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
"requires": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
}
},
"load-json-file": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
@@ -8551,6 +8552,19 @@
"integrity": "sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA==",
"dev": true
},
"optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"requires": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
}
},
"os-browserify": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
@@ -8567,6 +8581,11 @@
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"owasp-password-strength-test": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/owasp-password-strength-test/-/owasp-password-strength-test-1.3.0.tgz",
"integrity": "sha1-T2KeQpA+j20nmyMNZXq2HljkSxI="
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -10340,6 +10359,12 @@
"resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
"integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw=="
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"optional": true
},
"source-map-resolve": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
+13 -2
View File
@@ -37,11 +37,19 @@
"bundle-analyzer": "webpack --mode development --config webpack.config.js --profile --json > stats.json && webpack-bundle-analyzer stats.json",
"update-buster": "node bin/generateCacheBuster.js >tmp/cacheBusterToken",
"coverage": "cat ./coverage/lcov.info | env-cmd -f ./ci.test.env codacy-coverage",
"dev": "env-cmd -f ./my.env nodemon server.js 0.0.0.0",
"dev": "env-cmd -f ./my.env nodemon --inspect server.js 0.0.0.0",
"dev-test": "env-cmd -f ./my.devtest.env nodemon --inspect server.js 0.0.0.0",
"prod": "env-cmd -f ./my.prod.env node server.js 0.0.0.0",
"lint": "eslint lib"
},
"main": "server.js",
"nodemonConfig": {
"ignore": [
"tests/*",
"node_modules/*",
"bin/*"
]
},
"config": {
"blanket": {
"pattern": [
@@ -75,6 +83,7 @@
"cssmin": "^0.4.3",
"csv-stringify": "^5.5.1",
"d3": "^5.16.0",
"dompurify": "^2.2.6",
"easyxml": "^2.0.1",
"ejs": "^2.7.4",
"errorhandler": "^1.5.1",
@@ -89,7 +98,7 @@
"jquery-ui-bundle": "^1.12.1-migrate",
"jquery.tooltips": "^1.0.0",
"js-storage": "^1.1.0",
"jsdom": "~11.11.0",
"jsdom": "^11.11.0",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.20",
"memory-cache": "^0.2.0",
@@ -102,6 +111,7 @@
"mongodb": "^3.6.0",
"mongomock": "^0.1.2",
"node-cache": "^4.2.1",
"owasp-password-strength-test": "^1.3.0",
"parse-duration": "^0.1.3",
"pem": "^1.14.4",
"pushover-notifications": "^1.2.2",
@@ -122,6 +132,7 @@
"webpack-cli": "^3.3.12"
},
"devDependencies": {
"axios": "^0.21.1",
"babel-eslint": "^10.1.0",
"benv": "^3.3.0",
"codacy-coverage": "^3.4.0",
+14 -1
View File
@@ -49,7 +49,7 @@ input[type=number]:invalid {
text-decoration: underline;
}
#treatmentDrawer {
#treatmentDrawer, #adminNotifiesDrawer {
background-color: #666;
border-left: 1px solid #999;
box-shadow: inset 4px 4px 5px 0 rgba(50, 50, 50, 0.75);
@@ -66,6 +66,19 @@ input[type=number]:invalid {
z-index: 1;
}
#adminNotifyContent {
margin: 10px;
}
.adminNotifyMessage {
margin-left: 10px;
}
.adminNotifyMessageAdditionalInfo {
margin-left: 10px;
font-size: 11px;
}
#treatmentDrawer input {
box-sizing: border-box;
}
+5 -4
View File
@@ -9,15 +9,15 @@
font-style: normal;
}
/*
/*
Icon font for additional plugin icons.
Please read assets/fonts/README.md about update process
*/
@font-face {
font-family: 'pluginicons';
/* Plugin Icons font files content (from WOFF and SVG icon files, base64 encoded) */
src: url(data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAWAAAsAAAAABTQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAABCAAAAGAAAABgDxIE8mNtYXAAAAFoAAAAVAAAAFQXVdKJZ2FzcAAAAbwAAAAIAAAACAAAABBnbHlmAAABxAAAAUgAAAFIFA4eR2hlYWQAAAMMAAAANgAAADYXVLrVaGhlYQAAA0QAAAAkAAAAJAdQA8ZobXR4AAADaAAAABQAAAAUCY4AAGxvY2EAAAN8AAAADAAAAAwAKAC4bWF4cAAAA4gAAAAgAAAAIAAJAFxuYW1lAAADqAAAAbYAAAG2DBt7mXBvc3QAAAVgAAAAIAAAACAAAwAAAAMCxwGQAAUAAAKZAswAAACPApkCzAAAAesAMwEJAAAAAAAAAAAAAAAAAAAAARAAAAAAAAAAAAAAAAAAAAAAQAAA6QEDwP/AAEADwABAAAAAAQAAAAAAAAAAAAAAIAAAAAAAAwAAAAMAAAAcAAEAAwAAABwAAwABAAAAHAAEADgAAAAKAAgAAgACAAEAIOkB//3//wAAAAAAIOkB//3//wAB/+MXAwADAAEAAAAAAAAAAAAAAAEAAf//AA8AAQAAAAAAAAAAAAIAADc5AQAAAAABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAwAA/8ADjgPAABsAOgBZAAABIgcOAQcGFRQXHgEXFjMyNz4BNzY1NCcuAScmARUUFx4BFxYzMjc+ATc2PQEUBw4BBwYjIicuAScmNREVFBceARcWMzI3PgE3Nj0BFAcOAQcGIyInLgEnJjUBx15TU3skJCQke1NTXl5TU3wjJCQjfFNT/dskJHtTU15eU1N8IyQkI3xTU15eU1N7JCQkJHtTU15eU1N8IyQkI3xTU15eU1N7JCQDwBISPikpMC8pKj0SEhISPSopLzApKT4SEv6rqy8qKT4SEhISPikqL6svKik+EhISEj4pKi/+46owKSk+EhISEj4pKTCqLykqPhESEhE+KikvAAAAAAEAAAABAABgRbaTXw889QALBAAAAAAA2lO7LAAAAADaU7ssAAD/wAOOA8AAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAA44AAQAAAAAAAAAAAAAAAAAAAAUEAAAAAAAAAAAAAAACAAAAA44AAAAAAAAACgAUAB4ApAABAAAABQBaAAMAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEACwAAAAEAAAAAAAIABwCEAAEAAAAAAAMACwBCAAEAAAAAAAQACwCZAAEAAAAAAAUACwAhAAEAAAAAAAYACwBjAAEAAAAAAAoAGgC6AAMAAQQJAAEAFgALAAMAAQQJAAIADgCLAAMAAQQJAAMAFgBNAAMAAQQJAAQAFgCkAAMAAQQJAAUAFgAsAAMAAQQJAAYAFgBuAAMAAQQJAAoANADUcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzcGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzUmVndWxhcgBSAGUAZwB1AGwAYQBycGx1Z2luaWNvbnMAcABsAHUAZwBpAG4AaQBjAG8AbgBzRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format('woff'),
url(data:application/font-svg;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIiA+DQo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+DQo8bWV0YWRhdGE+R2VuZXJhdGVkIGJ5IEljb01vb248L21ldGFkYXRhPg0KPGRlZnM+DQo8Zm9udCBpZD0icGx1Z2luaWNvbnMiIGhvcml6LWFkdi14PSIxMDI0Ij4NCjxmb250LWZhY2UgdW5pdHMtcGVyLWVtPSIxMDI0IiBhc2NlbnQ9Ijk2MCIgZGVzY2VudD0iLTY0IiAvPg0KPG1pc3NpbmctZ2x5cGggaG9yaXotYWR2LXg9IjEwMjQiIC8+DQo8Z2x5cGggdW5pY29kZT0iJiN4MjA7IiBob3Jpei1hZHYteD0iNTEyIiBkPSIiIC8+DQo8Z2x5cGggdW5pY29kZT0iJiN4ZTkwMTsiIGdseXBoLW5hbWU9ImRhdGFiYXNlIiBob3Jpei1hZHYteD0iOTEwIiBkPSJNNDU1LjExMSA5NjBjLTI1MS40NDkgMC00NTUuMTExLTEwMS44MzEtNDU1LjExMS0yMjcuNTU2czIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTYgNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1Ni0yMDMuNjYyIDIyNy41NTYtNDU1LjExMSAyMjcuNTU2ek0wIDYxOC42Njd2LTE3MC42NjdjMC0xMjUuNzI0IDIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTZzNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1NnYxNzAuNjY3YzAtMTI1LjcyNC0yMDMuNjYyLTIyNy41NTYtNDU1LjExMS0yMjcuNTU2cy00NTUuMTExIDEwMS44MzEtNDU1LjExMSAyMjcuNTU2ek0wIDMzNC4yMjJ2LTE3MC42NjdjMC0xMjUuNzI0IDIwMy42NjItMjI3LjU1NiA0NTUuMTExLTIyNy41NTZzNDU1LjExMSAxMDEuODMxIDQ1NS4xMTEgMjI3LjU1NnYxNzAuNjY3YzAtMTI1LjcyNC0yMDMuNjYyLTIyNy41NTYtNDU1LjExMS0yMjcuNTU2cy00NTUuMTExIDEwMS44MzEtNDU1LjExMSAyMjcuNTU2eiIgLz4NCjwvZm9udD48L2RlZnM+PC9zdmc+) format('svg');
src: url(data:application/font-woff;charset=utf-8;base64,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) format('woff'),
url(data:application/font-svg;charset=utf-8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBzdGFuZGFsb25lPSJubyI/Pgo8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiID4KPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8bWV0YWRhdGE+R2VuZXJhdGVkIGJ5IEljb01vb248L21ldGFkYXRhPgo8ZGVmcz4KPGZvbnQgaWQ9Imljb21vb24iIGhvcml6LWFkdi14PSIxMDI0Ij4KPGZvbnQtZmFjZSB1bml0cy1wZXItZW09IjEwMjQiIGFzY2VudD0iOTYwIiBkZXNjZW50PSItNjQiIC8+CjxtaXNzaW5nLWdseXBoIGhvcml6LWFkdi14PSIxMDI0IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4MjA7IiBob3Jpei1hZHYteD0iNTEyIiBkPSIiIC8+CjxnbHlwaCB1bmljb2RlPSImI3hlOTAxOyIgZ2x5cGgtbmFtZT0iZGF0YWJhc2UiIGhvcml6LWFkdi14PSI5MTAiIGQ9Ik00NTUuMTExIDk2MGMtMjUxLjQ0OSAwLTQ1NS4xMTEtMTAxLjgzMS00NTUuMTExLTIyNy41NTZzMjAzLjY2Mi0yMjcuNTU2IDQ1NS4xMTEtMjI3LjU1NiA0NTUuMTExIDEwMS44MzEgNDU1LjExMSAyMjcuNTU2LTIwMy42NjIgMjI3LjU1Ni00NTUuMTExIDIyNy41NTZ6TTAgNjE4LjY2N3YtMTcwLjY2N2MwLTEyNS43MjQgMjAzLjY2Mi0yMjcuNTU2IDQ1NS4xMTEtMjI3LjU1NnM0NTUuMTExIDEwMS44MzEgNDU1LjExMSAyMjcuNTU2djE3MC42NjdjMC0xMjUuNzI0LTIwMy42NjItMjI3LjU1Ni00NTUuMTExLTIyNy41NTZzLTQ1NS4xMTEgMTAxLjgzMS00NTUuMTExIDIyNy41NTZ6TTAgMzM0LjIyMnYtMTcwLjY2N2MwLTEyNS43MjQgMjAzLjY2Mi0yMjcuNTU2IDQ1NS4xMTEtMjI3LjU1NnM0NTUuMTExIDEwMS44MzEgNDU1LjExMSAyMjcuNTU2djE3MC42NjdjMC0xMjUuNzI0LTIwMy42NjItMjI3LjU1Ni00NTUuMTExLTIyNy41NTZzLTQ1NS4xMTEgMTAxLjgzMS00NTUuMTExIDIyNy41NTZ6IiAvPgo8Z2x5cGggdW5pY29kZT0iJiN4ZTkxYTsiIGdseXBoLW5hbWU9Im5vdGlmaWVzIiBkPSJNMTAyNCA1MzAuNzQ0YzAgMjAwLjkyNi01OC43OTIgMzYzLjkzOC0xMzEuNDgyIDM2NS4yMjYgMC4yOTIgMC4wMDYgMC41NzggMC4wMzAgMC44NzIgMC4wMzBoLTgyLjk0MmMwIDAtMTk0LjgtMTQ2LjMzNi00NzUuMjMtMjAzLjc1NC04LjU2LTQ1LjI5Mi0xNC4wMzAtOTkuMjc0LTE0LjAzMC0xNjEuNTAyczUuNDY2LTExNi4yMDggMTQuMDMwLTE2MS41YzI4MC40MjgtNTcuNDE4IDQ3NS4yMy0yMDMuNzU2IDQ3NS4yMy0yMDMuNzU2aDgyLjk0MmMtMC4yOTIgMC0wLjU3OCAwLjAyNC0wLjg3MiAwLjAzMiA3Mi42OTYgMS4yODggMTMxLjQ4MiAxNjQuMjk4IDEzMS40ODIgMzY1LjIyNHpNODY0LjgyNCAyMjAuNzQ4Yy05LjM4MiAwLTE5LjUzMiA5Ljc0Mi0yNC43NDYgMTUuNTQ4LTEyLjYzIDE0LjA2NC0yNC43OTIgMzUuOTYtMzUuMTg4IDYzLjMyOC0yMy4yNTYgNjEuMjMyLTM2LjA2NiAxNDMuMzEtMzYuMDY2IDIzMS4xMjQgMCA4Ny44MSAxMi44MSAxNjkuODkgMzYuMDY2IDIzMS4xMjIgMTAuMzk0IDI3LjM2OCAyMi41NjIgNDkuMjY2IDM1LjE4OCA2My4zMjggNS4yMTQgNS44MTIgMTUuMzY0IDE1LjU1MiAyNC43NDYgMTUuNTUyIDkuMzggMCAxOS41MzYtOS43NDQgMjQuNzQ0LTE1LjU1MiAxMi42MzQtMTQuMDY0IDI0Ljc5Ni0zNS45NTggMzUuMTg4LTYzLjMyOCAyMy4yNTgtNjEuMjMgMzYuMDY4LTE0My4zMTIgMzYuMDY4LTIzMS4xMjIgMC04Ny44MDQtMTIuODEtMTY5Ljg4OC0zNi4wNjgtMjMxLjEyNC0xMC4zOS0yNy4zNjgtMjIuNTYyLTQ5LjI2NC0zNS4xODgtNjMuMzI4LTUuMjA4LTUuODA2LTE1LjM2LTE1LjU0OC0yNC43NDQtMTUuNTQ4ek0yNTEuODEyIDUzMC43NDRjMCA1MS45NSAzLjgxIDEwMi40MyAxMS4wNTIgMTQ5LjA5NC00Ny4zNzItNi41NTQtODguOTQyLTEwLjMyNC0xNDAuMzQtMTAuMzI0LTY3LjA1OCAwLTY3LjA1OCAwLTY3LjA1OCAwbC01NS40NjYtOTQuNjg2di04OC4xN2w1NS40Ni05NC42ODZjMCAwIDAgMCA2Ny4wNjAgMCA1MS4zOTggMCA5Mi45NjgtMy43NzQgMTQwLjM0LTEwLjMyNC03LjIzNiA0Ni42NjQtMTEuMDQ4IDk3LjE0Ni0xMS4wNDggMTQ5LjA5NnpNMzY4LjE1IDMxNy44MjhsLTEyNy45OTggMjQuNTEgODEuODQyLTMyMS41NDRjNC4yMzYtMTYuNjM0IDIwLjc0NC0yNS4wMzggMzYuNjg2LTE4LjY1NGwxMTguNTU2IDQ3LjQ1MmMxNS45NDQgNi4zNzYgMjIuMzI4IDIzLjk2NCAxNC4xOTYgMzkuMDg0bC0xMjMuMjgyIDIyOS4xNTJ6TTg2NC44MjQgNDExLjI3Yy0zLjYxOCAwLTcuNTI4IDMuNzU0LTkuNTM4IDUuOTkyLTQuODcgNS40Mi05LjU1NiAxMy44Ni0xMy41NjIgMjQuNDA4LTguOTYyIDIzLjYtMTMuOSA1NS4yMzQtMTMuOSA4OS4wNzhzNC45MzggNjUuNDc4IDEzLjkgODkuMDc4YzQuMDA2IDEwLjU0OCA4LjY5NiAxOC45ODggMTMuNTYyIDI0LjQwOCAyLjAxMCAyLjI0IDUuOTIgNS45OTQgOS41MzggNS45OTQgMy42MTYgMCA3LjUzLTMuNzU2IDkuNTM4LTUuOTk0IDQuODctNS40MiA5LjU1Ni0xMy44NTggMTMuNTYtMjQuNDA4IDguOTY0LTIzLjU5OCAxMy45MDItNTUuMjM0IDEzLjkwMi04OS4wNzggMC0zMy44NDItNC45MzgtNjUuNDc4LTEzLjkwMi04OS4wNzgtNC4wMDQtMTAuNTQ4LTguNjk2LTE4Ljk4OC0xMy41Ni0yNC40MDgtMi4wMDgtMi4yMzgtNS45Mi01Ljk5Mi05LjUzOC01Ljk5MnoiIC8+CjwvZm9udD48L2RlZnM+PC9zdmc+) format('svg');
font-weight: normal;
font-style: normal;
}
@@ -61,7 +61,7 @@
[class^="plugicon-"]:before, [class*=" plugicon-"]:before {
font-family: "pluginicons";
}
.icon-volume:before { content: '\e800'; }
.icon-plus:before { content: '\e801'; }
.icon-edit:before { content: '\e802'; }
@@ -85,6 +85,7 @@
/* Plugin Icons id-s (copy from generated icon style.css) */
.plugicon-database:before { content: "\e901"; }
.plugicon-notifies:before { content: "\e91a"; }
html, body {
margin: 0;
+39
View File
@@ -0,0 +1,39 @@
'use strict';
var _ = require('lodash');
var language = require('../lib/language')();
describe('Clean MONGO after tests', function ( ) {
this.timeout(10000);
var self = this;
var api = require('../lib/api/');
beforeEach(function (done) {
process.env.API_SECRET = 'this is my long pass phrase';
self.env = require('../env')();
self.env.settings.authDefaultRoles = 'readable';
self.env.settings.enable = ['careportal', 'api'];
this.wares = require('../lib/middleware/')(self.env);
self.app = require('express')();
self.app.enable('api');
require('../lib/server/bootevent')(self.env, language).boot(function booted(ctx) {
self.ctx = ctx;
self.ctx.ddata = require('../lib/data/ddata')();
self.app.use('/api', api(self.env, ctx));
done();
});
});
it('wipe treatment data', function (done) {
self.ctx.treatments().remove({ }, function ( ) {
done();
});
});
it('wipe entries data', function (done) {
self.ctx.entries().remove({ }, function ( ) {
done();
});
});
});
+30
View File
@@ -0,0 +1,30 @@
'use strict';
const should = require('should');
const ctx = {};
ctx.bus = {};
ctx.bus.on = function mockOn(channel, f) { };
const adminnotifies = require('../lib/adminnotifies')(ctx);
describe('adminnotifies', function ( ) {
it('should aggregate a message', function () {
const notify = {
title: 'Foo'
, message: 'Bar'
};
adminnotifies.addNotify(notify);
adminnotifies.addNotify(notify);
const notifies = adminnotifies.getNotifies();
notifies.length.should.equal(1);
});
});
+1 -1
View File
@@ -70,7 +70,7 @@ describe('admintools', function ( ) {
before(function (done) {
benv.setup(function() {
benv.require(__dirname + '/../tmp/js/bundle.report.js');
benv.require(__dirname + '/../tmp/js/bundle.app.js');
self.$ = $;
+1
View File
@@ -60,6 +60,7 @@ describe('Devicestatus API', function ( ) {
.set('api-secret', self.env.api_secret || '')
.expect(200)
.expect(function (response) {
console.log(JSON.stringify(response.body[0]));
response.body[0].xdripjs.state.should.equal(6);
response.body[0].utcOffset.should.equal(0);
})
+2 -2
View File
@@ -38,7 +38,7 @@ describe('Treatment API', function ( ) {
request(self.app)
.post('/api/treatments/')
.set('api-secret', self.env.api_secret || '')
.send({eventType: 'Meal Bolus', created_at: now, carbs: '30', insulin: '2.00', preBolus: '15', glucose: 100, glucoseType: 'Finger', units: 'mg/dl'})
.send({eventType: 'Meal Bolus', created_at: now, carbs: '30', insulin: '2.00', preBolus: '15', glucose: 100, glucoseType: 'Finger', units: 'mg/dl', notes: '<IMG SRC="javascript:alert(\'XSS\');">'})
.expect(200)
.end(function (err) {
if (err) {
@@ -50,10 +50,10 @@ describe('Treatment API', function ( ) {
});
sorted.length.should.equal(2);
sorted[0].glucose.should.equal(100);
sorted[0].notes.should.equal('<img>');
should.not.exist(sorted[0].eventTime);
sorted[0].insulin.should.equal(2);
sorted[1].carbs.should.equal(30);
done();
});
}
-1
View File
@@ -423,7 +423,6 @@ describe('API3 CREATE', function() {
delete doc._id; // APIv1 updates input document, we must get rid of _id for the next round
oldBody.should.containEql(doc);
const doc2 = Object.assign({}, doc, {
eventType: 'Meal Bolus',
insulin: 0.4,
+1 -2
View File
@@ -13,7 +13,7 @@ var nowData = {
};
describe('client', function ( ) {
this.timeout(30000); // TODO: see why this test takes longer on Travis to complete
this.timeout(40000); // TODO: see why this test takes longer on Travis to complete
var self = this;
@@ -47,7 +47,6 @@ describe('client', function ( ) {
next(true);
};
client.init();
client.dataUpdate(nowData, true);
+3
View File
@@ -86,6 +86,9 @@ ctx.ddata.devicestatus = [{uploader: {battery: 100}}];
var bootevent = require('../lib/server/bootevent');
describe('Pebble Endpoint', function ( ) {
this.timeout(10000);
var pebble = require('../lib/server/pebble');
before(function (done) {
var env = require('../env')( );
+8 -5
View File
@@ -220,8 +220,10 @@ describe('reports', function ( ) {
window.alert = function mockAlert () {
return true;
};
window.setTimeout = function mockSetTimeout (call) {
window.setTimeout = function mockSetTimeout (call, timer) {
if (timer == 60000) return;
call();
};
@@ -296,9 +298,10 @@ describe('reports', function ( ) {
window.alert = function mockAlert () {
return true;
};
window.setTimeout = function mockSetTimeout (call) {
call();
window.setTimeout = function mockSetTimeout (call, timer) {
if (timer == 60000) return;
call();
};
client.init(function afterInit ( ) {
+1 -1
View File
@@ -64,7 +64,7 @@ describe('API_SECRET', function ( ) {
process.env.API_SECRET = 'tooshort';
var env = require('../env')( );
should.not.exist(env.api_secret);
env.err.desc.should.startWith('API_SECRET should be at least');
env.err[0].desc.should.startWith('API_SECRET should be at least');
});
function ping_status (app, fn) {
+8
View File
@@ -655,6 +655,11 @@
,"virtAsstDatabaseSize":"%1 MiB. That is %2% of available database space."
,"virtAsstTitleDatabaseSize":"Database file size"
,"Carbs/Food/Time":"Carbs/Food/Time"
,"You have administration messages":"You have administration messages"
,"Admin messages in queue":"Admin messages in queue"
,"Queue empty":"Queue empty"
,"There are no admin messages in queue":"There are no admin messages in queue"
,"Please sign in using the API_SECRET to see your administration messages":"Please sign in using the API_SECRET to see your administration messages"
,"Reads enabled in default permissions":"Reads enabled in default permissions"
,"Data reads enabled": "Data reads enabled"
,"Data writes enabled": "Data writes enabled"
@@ -685,4 +690,7 @@
,"view without token":"view without token"
,"Remove stored token":"Remove stored token"
,"Weekly Distribution":"Weekly Distribution"
,"Failed authentication":"Failed authentication"
,"A device at IP number":"A device at IP number"
,"attempted authenticating with Nightscout with wrong credentials. Check if you have an uploader setup with wrong API_SECRET or token?":"attempted authenticating with Nightscout with wrong credentials. Check if you have an uploader setup with wrong API_SECRET or token?"
}
+3
View File
@@ -302,6 +302,9 @@
</form>
</div>
<div id="adminNotifiesDrawer" class="drawer">
</div>
<div id="treatmentDrawer">
<form id="treatment-form">
<fieldset class="treatmentData">
+1
View File
@@ -23,6 +23,7 @@
<a id="drawerToggle" class="tip" original-title="Settings" aria-label="Settings" href="#"><i class="icon-menu"></i></a>
<a id="testAlarms" class="tip" original-title="Alarm Test / Smartphone Enable" aria-label="Alarm Test / Smartphone Enable" href="#"><i class="icon-volume"></i></a>
<a id="editbutton" class="tip" original-title="Edit Mode" aria-label="Edit Mode" href="#" style="display:none;"><i class="icon-edit"></i></a>
<a id="adminnotifies" class="tip" original-title="Admin ntofications" aria-label="Admin notifications" href="#" style="display:none;"><i class="plugicon-notifies"></i></a>
</div>
<% } %>
</div>