import { Eltro as t, assert} from 'eltro' import { HttpError } from 'flaska' import { createContext } from '../helper.server.mjs' import { verifyToken, verifyBody, throwIfNotPublic } from '../../api/media/security.mjs' import encode from '../../api/jwt/encode.mjs' import config from '../../api/config.mjs' t.describe('#throwIfNotPublic()', function() { let backup = {} t.before(function() { backup = config.sources[1].store config.sources[1].store = { sites: { justatest: { }, justatest2: { public: false, }, justatest3: { public: true, }, }, } }) t.after(function() { config.sources[1].store = backup }) t.test('should throw for sites that do not exist or are null', function() { let tests = [ 'justatest', 'justatest2', 'nonexisting1', null, ] tests.forEach(function(test) { assert.throws(function() { throwIfNotPublic(test) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 404) assert.match(err.message, new RegExp(test)) assert.match(err.message, /exist/i) return true }, `should throw with site ${test}`) }) }) t.test('should pass for sites that allow public listing', function() { let tests = [ 'justatest3', ] tests.forEach(function(test) { assert.doesNotThrow(function() { throwIfNotPublic(test) }, `should not throw with site ${test}`) }) }) }) t.describe('#verifyToken()', function() { let backup = {} t.before(function() { backup = config.sources[1].store config.sources[1].store = { sites: { justatest: { keys: { 'default@HS512': 'mysharedkey', } }, }, } }) t.after(function() { config.sources[1].store = backup }) t.test('should fail if query token is missing', function() { let ctx = createContext({ }) ctx.query.delete('token') assert.throws(function() { verifyToken(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /query/i) assert.match(err.message, /token/i) return true }) }) function assertInvalidToken(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /invalid/i) assert.match(err.message, /token/i) return true } t.test('should fail if token is invalid', function() { let ctx = createContext({ }) ctx.query.set('token', 'asdfasdgassdga') assert.throws(function() { verifyToken(ctx) }, assertInvalidToken) assert.ok(ctx.log.error.lastCall) assert.match(ctx.log.error.lastCall[0].message, /3 dots/) ctx.query.set('token', 'asdfasdgassdga.asdfasdg.sadfsadfas') assert.throws(function() { verifyToken(ctx) }, assertInvalidToken) assert.match(ctx.log.error.lastCall[0].message, /invalid/i) ctx.query.set('token', encode( { typ: 'JWT', alg: 'HS256' }, { iss: 'justatest' }, 'mysharedkey' )) assert.throws(function() { verifyToken(ctx) }, assertInvalidToken) assert.match(ctx.log.error.lastCall[0].message, /pubkey/) ctx.query.set('token', encode( { typ: 'JWT', alg: 'HS512' }, { iss: 'notexist' }, 'mysharedkey' )) assert.throws(function() { verifyToken(ctx) }, assertInvalidToken) assert.match(ctx.log.error.lastCall[0].message, /notexist/) ctx.query.set('token', encode( { typ: 'JWT', alg: 'HS512' }, { iss: 'justatest' }, 'mysharedkey2' )) assert.throws(function() { verifyToken(ctx) }, assertInvalidToken) assert.match(ctx.log.error.lastCall[0].message, /HS512/) assert.match(ctx.log.error.lastCall[0].message, /Verification/i) }) t.test('should otherwise return the issuer', function() { let ctx = createContext({ }) ctx.query.set('token', encode( { typ: 'JWT', alg: 'HS512' }, { iss: 'justatest' }, 'mysharedkey' )) let site = verifyToken(ctx) assert.strictEqual(site, 'justatest') }) }) t.describe('#verifyBody()', function() { t.test('should succeed with empty body', function() { let ctx = createContext({ req: { body: { } } }) verifyBody(ctx) }) t.test('should fail with invalid body', function() { let ctx = createContext({ req: { body: { item: {} } } }) let tests = [ [null, 'null'], ['', 'empty string'], ['asdf', 'string'], [0, 'empty number'], [123, 'number'], [[], 'array'], ] tests.forEach(function (check) { ctx.req.body.item = check[0] assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /item/i) assert.match(err.message, /valid/i) return true }, `should fail if body entry is ${check[1]}`) }) }) let testInvalidNames = ['filename', 'path'] testInvalidNames.forEach(function(invalidName) { t.test(`should fail if an item has the name ${invalidName}`, function() { let ctx = createContext({ req: { body: { [invalidName]: {} } } }) assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /name/i) assert.match(err.message, new RegExp(invalidName)) assert.match(err.message, /allowed/i) return true }, 'should fail if body item has the name original') }) }) t.test('should require format string present in item', function() { let ctx = createContext({ req: { body: { item: {} } } }) let tests = [ [undefined, 'undefined'], [null, 'null'], ['', 'empty string'], [{}, 'object'], [0, 'empty number'], [123, 'number'], [[], 'array'], ['resize', 'not allow resize'], ['out', 'not allow out'], ] tests.forEach(function (check) { ctx.req.body.item = { format: check[0], } assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /format/i) assert.match(err.message, /missing/i) return true }, `should fail if body item format is ${check[1]}`) }) }) t.test('should require object of same name as format', function() { let ctx = createContext({ req: { body: { item: {} } } }) let tests = [ [undefined, 'undefined'], [null, 'null'], ['', 'emptystring'], ['asdf', 'string'], [0, 'emptynumber'], [123, 'number'], [[], 'array'], ] tests.forEach(function (check) { ctx = createContext({ req: { body: { item: {} } } }) ctx.req.body.item.format = check[1] ctx.req.body.item[check[1]] = check[0] assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /format/i) assert.match(err.message, /options/i) assert.match(err.message, /valid/i) return true }, `should fail if body item format options is ${check[1]}`) }) }) t.test('should allow empty value or string in out in item', function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ [undefined, 'undefined'], [null, 'null'], ['', 'empty string'], ['file', 'allow string file'], ['base64', 'allow base64'], ] tests.forEach(function (check) { ctx.req.body.item.out = check[0] assert.doesNotThrow(function() { verifyBody(ctx) }, `should not throw with ${check[1]} in out`) }) }) t.test('should fail if out is invalid value', function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ [{}, 'object'], [0, 'empty number'], [123, 'number'], [[], 'array'], ['resize', 'not allow resize'], ['out', 'not allow out'], ['example', 'not allow example'], ] tests.forEach(function (check) { ctx.req.body.item.out = check[0] assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /item/i) assert.match(err.message, /out/i) assert.match(err.message, /valid/i) return true }, `should fail if body item out is ${check[1]}`) }) }) let validObjectOperations = [ 'resize', 'extend', 'flatten', ] validObjectOperations.forEach(function(operation) { t.test(`should allow empty value or object in ${operation}`, function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ [undefined, 'undefined'], [null, 'null'], [{}, 'object'], ] tests.forEach(function (check) { ctx.req.body.item[operation] = check[0] assert.doesNotThrow(function() { verifyBody(ctx) }, `should not throw with ${check[1]} in ${operation}`) }) }) t.test(`should fail if ${operation} if specified is invalid`, function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ ['', 'emptystring'], ['asdf', 'string'], [0, 'emptynumber'], [123, 'number'], [[], 'array'], ] tests.forEach(function (check) { ctx.req.body.item[operation] = check[0] assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /item/i) assert.match(err.message, new RegExp(operation)) assert.match(err.message, /valid/i) return true }, `should fail if body item ${operation} is ${check[1]}`) }) }) }) let validNumberOperations = [ 'blur', 'trim', ] validNumberOperations.forEach(function(operation) { t.test(`should allow empty value or number in ${operation}`, function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ [undefined, 'undefined'], [null, 'null'], [0, 'number'], [0.5, 'positive number'], ] tests.forEach(function (check) { ctx.req.body.item[operation] = check[0] assert.doesNotThrow(function() { verifyBody(ctx) }, `should not throw with ${check[1]} in ${operation}`) }) }) t.test(`should fail if ${operation} if specified is invalid`, function() { let ctx = createContext({ req: { body: { item: { format: 'test', test: {}, } } } }) let tests = [ ['', 'emptystring'], ['asdf', 'string'], [{}, 'object'], [[], 'array'], ] tests.forEach(function (check) { ctx.req.body.item[operation] = check[0] assert.throws(function() { verifyBody(ctx) }, function(err) { assert.ok(err instanceof HttpError) assert.ok(err instanceof Error) assert.strictEqual(err.status, 422) assert.match(err.message, /body/i) assert.match(err.message, /item/i) assert.match(err.message, new RegExp(operation)) assert.match(err.message, /valid/i) return true }, `should fail if body item ${operation} is ${check[1]}`) }) }) }) })