diff --git a/.gitignore b/.gitignore index 0772d0d..d950511 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ data/* +!data/cc8/ docker-compose.override.yml .idea diff --git a/data/cc8/tluICx+fpoDNb+DsLMquWl+aai5sVg/regression-control.txt b/data/cc8/tluICx+fpoDNb+DsLMquWl+aai5sVg/regression-control.txt new file mode 100644 index 0000000..0a8d78d Binary files /dev/null and b/data/cc8/tluICx+fpoDNb+DsLMquWl+aai5sVg/regression-control.txt differ diff --git a/index.js b/index.js index 492703d..8078c6d 100644 --- a/index.js +++ b/index.js @@ -241,3 +241,5 @@ http.createServer((req, res) => { }).listen(1234); + +module.exports = server; diff --git a/package-lock.json b/package-lock.json index cc7d741..bc45e9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -88,6 +88,12 @@ "concat-map": "0.0.1" } }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -111,6 +117,18 @@ "delayed-stream": "1.0.0" } }, + "commander": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", + "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -133,6 +151,12 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" }, + "cookiejar": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.1.tgz", + "integrity": "sha1-Qa1XsbVVlR7BcUEqgZQrHoIA00o=", + "dev": true + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -170,9 +194,9 @@ } }, "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" @@ -183,6 +207,12 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "dev": true + }, "ecc-jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", @@ -207,6 +237,12 @@ "es6-promise": "4.2.4" } }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", @@ -219,7 +255,6 @@ "dev": true, "requires": { "concat-stream": "1.6.0", - "debug": "2.6.9", "mkdirp": "0.5.0", "yauzl": "2.4.1" }, @@ -313,6 +348,12 @@ "path-is-absolute": "1.0.1" } }, + "growl": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", + "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", + "dev": true + }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", @@ -327,6 +368,12 @@ "har-schema": "2.0.0" } }, + "has-flag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", + "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", + "dev": true + }, "hawk": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", @@ -338,6 +385,12 @@ "sntp": "2.1.0" } }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", + "dev": true + }, "hoek": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", @@ -453,6 +506,12 @@ "is-buffer": "1.1.6" } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=", + "dev": true + }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -501,6 +560,25 @@ } } }, + "mocha": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.1.1.tgz", + "integrity": "sha512-kKKs/H1KrMMQIEsWNxGmb4/BGsmj0dkeyotEvbrAuQ01FcWRLssUNXCEUZk6SZtyJBi6EE7SL0zDDtItw1rGhw==", + "dev": true, + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.11.0", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.3", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "4.4.0" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -567,7 +645,6 @@ "integrity": "sha512-wx10aPQPpGJVxdB6yoDSLm9p4rCwARUSLMVV0bx++owuqkvviXKyiFM3EWsywaFmjOKNPXacIjplF7xhHiFP3w==", "dev": true, "requires": { - "debug": "2.6.9", "extract-zip": "1.6.6", "https-proxy-agent": "2.2.1", "mime": "1.6.0", @@ -709,6 +786,43 @@ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" }, + "superagent": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", + "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "dev": true, + "requires": { + "component-emitter": "1.2.1", + "cookiejar": "2.1.1", + "debug": "3.1.0", + "extend": "3.0.1", + "form-data": "2.3.2", + "formidable": "1.2.1", + "methods": "1.1.2", + "mime": "1.6.0", + "qs": "6.5.1", + "readable-stream": "2.3.6" + } + }, + "supertest": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-3.0.0.tgz", + "integrity": "sha1-jUu2j9GDDuBwM7HFpamkAhyWUpY=", + "dev": true, + "requires": { + "methods": "1.1.2", + "superagent": "3.8.3" + } + }, + "supports-color": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", + "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", + "dev": true, + "requires": { + "has-flag": "2.0.0" + } + }, "timed-out": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", diff --git a/package.json b/package.json index c87c4b0..a62d322 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "slug": "^0.9.1" }, "devDependencies": { - "puppeteer": "^1.3.0" + "mocha": "^5.1.1", + "puppeteer": "^1.3.0", + "supertest": "^3.0.0" }, "scripts": { "test": "mocha" diff --git a/public/js/cipher.js b/public/js/cipher.js index fa5ed59..7bcf00c 100644 --- a/public/js/cipher.js +++ b/public/js/cipher.js @@ -54,12 +54,12 @@ const encryptFiles = async ([files, hashKey]) => { }); }; -const decryptFile = async (file) => { +const decryptFile = async ([file, hashKey]) => { const key = await window.crypto.subtle.importKey( "jwk", { kty: "oct", - k: window.location.hash.slice(1), + k: hashKey, alg: "A256GCM", ext: true, }, @@ -83,7 +83,8 @@ const decryptFile = async (file) => { key, data ); - resolve(window.URL.createObjectURL(new Blob([decryptedFileRaw]))); + // resolve(window.URL.createObjectURL(new Blob([decryptedFileRaw]))); + resolve(new Blob([decryptedFileRaw])); } reader.readAsArrayBuffer(file); }); diff --git a/public/js/dir.js b/public/js/dir.js index 060671f..b549b37 100644 --- a/public/js/dir.js +++ b/public/js/dir.js @@ -3,6 +3,18 @@ //TODO: avoid global variables... let content = ''; //list of uploaded files. +/*#############################################################| +| TESTS +*##############################################################*/ + +const regressionTest = () => { + window.location.hash = ''; + window.location.href = window.location.origin + '/dir/data/cc8/tluICx+fpoDNb+DsLMquWl+aai5sVg#NfF6mwHOSZire9yal1dlOMFn67RTD8tsL_mjlsPP_Is'; + // Gian: we need the above reload for consecutive tests, since the page wont reload when the path doesn't change (only the hash is changing after the first test). We also need the delay since without it the reload happens before the url (with the new hash) is updated. + setTimeout(() => { + window.location.reload(); + }, 500); +} /*#############################################################| | VALIDATION @@ -41,6 +53,25 @@ if (window.location.protocol === 'http:') { /* File download =========================================== */ +const beforeDownload = blob => { + if (window.location.hash === '#NfF6mwHOSZire9yal1dlOMFn67RTD8tsL_mjlsPP_Is') { + return new Promise((resolve, reject) => { + let reader = new FileReader(); + reader.onload = (e => { + if (e.target.result !== 'bar') { + throw new Error('Regression test failed.'); + } else { + console.log('Success!'); + } + resolve('#'); // this prevents page refresh on click + }); + reader.readAsText(blob); + }); + } else { + return window.URL.createObjectURL(blob); + } +} + const handleFileDownload = (e) => { if (window.location.hash) { @@ -50,7 +81,8 @@ const handleFileDownload = (e) => { let fetchFilePath = function (filePath, filename) { fetch(filePath) .then(res => res.blob()) - .then(blob => decryptFile(blob)) + .then(blob => decryptFile([blob, window.location.hash.slice(1)])) + .then(beforeDownload) .then(downloadLink => { currentLink.setAttribute('href', downloadLink); currentLink.setAttribute('download', filename); @@ -84,7 +116,7 @@ const handleFileDownload = (e) => { } else { //TODO: better feedback? let hashWarning = new Alert().showMessage("danger", "Error: no hash found."); - console.log('Error: no hash found.'); + console.error('Error: no hash found.'); } }; @@ -120,6 +152,10 @@ const addFilesDecrypt = () => { } + /* Triggers regression test =========================================== */ + if (window.location.hash === '#NfF6mwHOSZire9yal1dlOMFn67RTD8tsL_mjlsPP_Is') { + document.querySelector(".files-listing").firstElementChild.firstElementChild.children[1].click(); // trigger regression-control.txt + } }; @@ -195,7 +231,7 @@ const listingFiles = () => { .then(removeListItem(deleteItem, deleteItemName)) //remove item from file listing .catch(error => { let deleteError = new Alert().showMessage("danger", `Error while trying to delete your file: ${error}`); - console.log(error); + console.error(error); }); } }); @@ -463,4 +499,4 @@ shareLinkBtn.addEventListener('click', function (e) { Display.copyLinkClipboard(); -}); \ No newline at end of file +}); diff --git a/test/api.js b/test/api.js new file mode 100644 index 0000000..0ee475d --- /dev/null +++ b/test/api.js @@ -0,0 +1,81 @@ +const server = require('../index.js'); +const request = require('supertest')(server); +const fs = require('fs'); +const path = require('path'); + +describe('GET /mkdir', function() { + let secretPath = ''; + after('delete secret folder', function(done) { + fs.rmdir(secretPath.split('/').slice(2).join(path.sep), function(err) { + if (err) return done(err); + fs.rmdir(secretPath.split('/').slice(2, 4).join(path.sep), function(err) { + if (err) return done(err); + done(); + }); + }); + }); + + it('create secret folder', function(done) { + request.get('/mkdir') + .expect(302) + .end(function(err, res) { + if (err) return done(err); + secretPath = res.headers.location; + // console.log(secretPath); + done(); + }); + }); +}); + +describe('File operations', function() { + let secretPath = ''; + before('create secret folder', function(done) { + request.get('/mkdir') + .end(function(err, res) { + if (err) return done(err); + secretPath = res.headers.location; + // console.log(secretPath); + done(); + }); + }); + + after('delete secret folder', function(done) { + fs.rmdir(secretPath.split('/').slice(2).join(path.sep), function(err) { + if (err) return done(err); + fs.rmdir(secretPath.split('/').slice(2, 4).join(path.sep), function(err) { + if (err) return done(err); + done(); + }); + }); + }); + + describe('POST /secret/folder', function() { + it('upload file to secret folder', function(done) { + request.post(secretPath) + //Gian: using the regression-contol file here instead of creating one from scratch. + .attach('test-file', './data/cc8/tluICx+fpoDNb+DsLMquWl+aai5sVg/regression-control.txt') + .expect(200, done); + }); + }); + + describe('GET /ls', function() { + it('list files', function(done) { + request.get(secretPath.replace("dir", "ls")) + // .expect(200, done); + .expect(200) + .end(function(err, res) { + if (err) return done(err); + //Gian: uncomment the log below if you want to check the files on the list. Otherwise, we could end the test earlier with 'expect(200, done)'. + // console.log(res.text); + done(); + }); + }); + }); + + describe('DELETE /secret/folder/file', function() { + it('delete file', function(done) { + request.delete(secretPath + '/regression-control.txt') + .expect(204, done); + }); + }); +});