first commit
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const sinon = require('sinon')
|
||||
const { expect } = require('chai')
|
||||
const modulePath = '../../../../app/src/Features/Helpers/AuthorizationHelper'
|
||||
|
||||
describe('AuthorizationHelper', function () {
|
||||
beforeEach(function () {
|
||||
this.AuthorizationHelper = SandboxedModule.require(modulePath, {
|
||||
requires: {
|
||||
'./AdminAuthorizationHelper': (this.AdminAuthorizationHelper = {
|
||||
hasAdminAccess: sinon.stub().returns(false),
|
||||
}),
|
||||
'../../models/User': {
|
||||
UserSchema: {
|
||||
obj: {
|
||||
staffAccess: {
|
||||
publisherMetrics: {},
|
||||
publisherManagement: {},
|
||||
institutionMetrics: {},
|
||||
institutionManagement: {},
|
||||
groupMetrics: {},
|
||||
groupManagement: {},
|
||||
adminMetrics: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'../Project/ProjectGetter': (this.ProjectGetter = { promises: {} }),
|
||||
'../SplitTests/SplitTestHandler': (this.SplitTestHandler = {
|
||||
promises: {},
|
||||
}),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
describe('hasAnyStaffAccess', function () {
|
||||
it('with empty user', function () {
|
||||
const user = {}
|
||||
expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.false
|
||||
})
|
||||
|
||||
it('with no access user', function () {
|
||||
const user = { isAdmin: false, staffAccess: { adminMetrics: false } }
|
||||
expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.false
|
||||
})
|
||||
|
||||
it('with admin user', function () {
|
||||
const user = { isAdmin: true }
|
||||
this.AdminAuthorizationHelper.hasAdminAccess.returns(true)
|
||||
expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.false
|
||||
})
|
||||
|
||||
it('with staff user', function () {
|
||||
const user = { staffAccess: { adminMetrics: true, somethingElse: false } }
|
||||
this.AdminAuthorizationHelper.hasAdminAccess.returns(true)
|
||||
expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.true
|
||||
})
|
||||
|
||||
it('with non-staff user with extra attributes', function () {
|
||||
// make sure that staffAccess attributes not declared on the model don't
|
||||
// give user access
|
||||
const user = { staffAccess: { adminMetrics: false, somethingElse: true } }
|
||||
expect(this.AuthorizationHelper.hasAnyStaffAccess(user)).to.be.false
|
||||
})
|
||||
})
|
||||
|
||||
describe('isReviewerRoleEnabled', function () {
|
||||
it('with no reviewers and no split test', async function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves({
|
||||
reviewer_refs: {},
|
||||
owner_ref: 'ownerId',
|
||||
})
|
||||
this.SplitTestHandler.promises.getAssignmentForUser = sinon
|
||||
.stub()
|
||||
.resolves({
|
||||
variant: 'disabled',
|
||||
})
|
||||
expect(
|
||||
await this.AuthorizationHelper.promises.isReviewerRoleEnabled(
|
||||
'projectId'
|
||||
)
|
||||
).to.be.false
|
||||
})
|
||||
|
||||
it('with no reviewers and enabled split test', async function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves({
|
||||
reviewer_refs: {},
|
||||
owner_ref: 'userId',
|
||||
})
|
||||
this.SplitTestHandler.promises.getAssignmentForUser = sinon
|
||||
.stub()
|
||||
.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
expect(
|
||||
await this.AuthorizationHelper.promises.isReviewerRoleEnabled(
|
||||
'projectId'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('with reviewers and disabled split test', async function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves({
|
||||
reviewer_refs: [{ $oid: 'userId' }],
|
||||
})
|
||||
this.SplitTestHandler.promises.getAssignmentForUser = sinon
|
||||
.stub()
|
||||
.resolves({
|
||||
variant: 'default',
|
||||
})
|
||||
expect(
|
||||
await this.AuthorizationHelper.promises.isReviewerRoleEnabled(
|
||||
'projectId'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('with reviewers and enabled split test', async function () {
|
||||
this.ProjectGetter.promises.getProject = sinon.stub().resolves({
|
||||
reviewer_refs: [{ $oid: 'userId' }],
|
||||
})
|
||||
this.SplitTestHandler.promises.getAssignmentForUser = sinon
|
||||
.stub()
|
||||
.resolves({
|
||||
variant: 'enabled',
|
||||
})
|
||||
expect(
|
||||
await this.AuthorizationHelper.promises.isReviewerRoleEnabled(
|
||||
'projectId'
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
})
|
||||
})
|
||||
28
services/web/test/unit/src/HelperFiles/DiffHelperTests.js
Normal file
28
services/web/test/unit/src/HelperFiles/DiffHelperTests.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const { expect } = require('chai')
|
||||
const {
|
||||
stringSimilarity,
|
||||
} = require('../../../../app/src/Features/Helpers/DiffHelper')
|
||||
|
||||
describe('DiffHelper', function () {
|
||||
describe('stringSimilarity', function () {
|
||||
it('should have a ratio of 1 for identical strings', function () {
|
||||
expect(stringSimilarity('abcdef', 'abcdef')).to.equal(1.0)
|
||||
})
|
||||
|
||||
it('should have a ratio of 0 for completely different strings', function () {
|
||||
expect(stringSimilarity('abcdef', 'qmglzxv')).to.equal(0.0)
|
||||
})
|
||||
|
||||
it('should have a ratio of between 0 and 1 for strings that are similar', function () {
|
||||
const ratio = stringSimilarity('abcdef', 'abcdef@zxvkp')
|
||||
expect(ratio).to.equal(0.66)
|
||||
})
|
||||
|
||||
it('should reject non-string inputs', function () {
|
||||
expect(() => stringSimilarity(1, 'abc')).to.throw
|
||||
expect(() => stringSimilarity('abc', 2)).to.throw
|
||||
expect(() => stringSimilarity('abc', new Array(1000).fill('a').join('')))
|
||||
.to.throw
|
||||
})
|
||||
})
|
||||
})
|
||||
56
services/web/test/unit/src/HelperFiles/EmailHelperTests.js
Normal file
56
services/web/test/unit/src/HelperFiles/EmailHelperTests.js
Normal file
@@ -0,0 +1,56 @@
|
||||
const { expect } = require('chai')
|
||||
const {
|
||||
parseEmail,
|
||||
} = require('../../../../app/src/Features/Helpers/EmailHelper')
|
||||
|
||||
describe('EmailHelper', function () {
|
||||
it('should parse a single email', function () {
|
||||
const address = 'test@example.com'
|
||||
const expected = 'test@example.com'
|
||||
expect(parseEmail(address)).to.equal(expected)
|
||||
expect(parseEmail(address, true)).to.equal(expected)
|
||||
})
|
||||
|
||||
it('should parse a valid email address', function () {
|
||||
const address = '"Test Person" <test@example.com>'
|
||||
const expected = 'test@example.com'
|
||||
expect(parseEmail(address)).to.equal(null)
|
||||
expect(parseEmail(address, true)).to.equal(expected)
|
||||
})
|
||||
|
||||
it('should return null for garbage input', function () {
|
||||
const cases = [
|
||||
undefined,
|
||||
null,
|
||||
'',
|
||||
42,
|
||||
['test@example.com'],
|
||||
{},
|
||||
{ length: 42 },
|
||||
{ trim: true, match: true },
|
||||
{ toString: true },
|
||||
]
|
||||
for (const input of cases) {
|
||||
expect(parseEmail(input)).to.equal(null, input)
|
||||
expect(parseEmail(input, true)).to.equal(null, input)
|
||||
}
|
||||
})
|
||||
|
||||
it('should return null for an invalid single email', function () {
|
||||
const address = 'testexample.com'
|
||||
expect(parseEmail(address)).to.equal(null)
|
||||
expect(parseEmail(address, true)).to.equal(null)
|
||||
})
|
||||
|
||||
it('should return null for an invalid email address', function () {
|
||||
const address = '"Test Person" test@example.com>'
|
||||
expect(parseEmail(address)).to.equal(null)
|
||||
expect(parseEmail(address, true)).to.equal(null)
|
||||
})
|
||||
|
||||
it('should return null for a group of addresses', function () {
|
||||
const address = 'Group name:test1@example.com,test2@example.com;'
|
||||
expect(parseEmail(address)).to.equal(null)
|
||||
expect(parseEmail(address, true)).to.equal(null)
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,156 @@
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const MODULE_PATH = require('path').join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Helpers/SafeHTMLSubstitution.js'
|
||||
)
|
||||
|
||||
describe('SafeHTMLSubstitution', function () {
|
||||
let SafeHTMLSubstitution
|
||||
before(function () {
|
||||
SafeHTMLSubstitution = SandboxedModule.require(MODULE_PATH)
|
||||
})
|
||||
|
||||
describe('SPLIT_REGEX', function () {
|
||||
const CASES = {
|
||||
'PRE<0>INNER</0>POST': ['PRE', '0', 'INNER', 'POST'],
|
||||
'<0>INNER</0>': ['', '0', 'INNER', ''],
|
||||
'<0></0>': ['', '0', '', ''],
|
||||
'<0>INNER</0><0>INNER2</0>': ['', '0', 'INNER', '', '0', 'INNER2', ''],
|
||||
'<0><1>INNER</1></0>': ['', '0', '<1>INNER</1>', ''],
|
||||
'PLAIN TEXT': ['PLAIN TEXT'],
|
||||
}
|
||||
Object.entries(CASES).forEach(([input, output]) => {
|
||||
it(`should parse "${input}" as expected`, function () {
|
||||
expect(input.split(SafeHTMLSubstitution.SPLIT_REGEX)).to.deep.equal(
|
||||
output
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('render', function () {
|
||||
describe('substitution', function () {
|
||||
it('should substitute a single component', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>good</0>', [{ name: 'b' }])
|
||||
).to.equal('<b>good</b>')
|
||||
})
|
||||
|
||||
it('should substitute a single component as string', function () {
|
||||
expect(SafeHTMLSubstitution.render('<0>good</0>', ['b'])).to.equal(
|
||||
'<b>good</b>'
|
||||
)
|
||||
})
|
||||
|
||||
it('should substitute a single component twice', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>one</0><0>two</0>', [{ name: 'b' }])
|
||||
).to.equal('<b>one</b><b>two</b>')
|
||||
})
|
||||
|
||||
it('should substitute two components', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>one</0><1>two</1>', [
|
||||
{ name: 'b' },
|
||||
{ name: 'i' },
|
||||
])
|
||||
).to.equal('<b>one</b><i>two</i>')
|
||||
})
|
||||
|
||||
it('should substitute a single component with a class', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>text</0>', [
|
||||
{
|
||||
name: 'b',
|
||||
attrs: {
|
||||
class: 'magic',
|
||||
},
|
||||
},
|
||||
])
|
||||
).to.equal('<b class="magic">text</b>')
|
||||
})
|
||||
|
||||
it('should substitute two nested components', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0><1>nested</1></0>', [
|
||||
{ name: 'b' },
|
||||
{ name: 'i' },
|
||||
])
|
||||
).to.equal('<b><i>nested</i></b>')
|
||||
})
|
||||
|
||||
it('should handle links', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>Go to Login</0>', [
|
||||
{ name: 'a', attrs: { href: 'https://www.overleaf.com/login' } },
|
||||
])
|
||||
).to.equal('<a href="https://www.overleaf.com/login">Go to Login</a>')
|
||||
})
|
||||
|
||||
it('should not complain about too many components', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>good</0>', [
|
||||
{ name: 'b' },
|
||||
{ name: 'i' },
|
||||
{ name: 'u' },
|
||||
])
|
||||
).to.equal('<b>good</b>')
|
||||
})
|
||||
})
|
||||
|
||||
describe('pug.escape', function () {
|
||||
it('should handle plain text', function () {
|
||||
expect(SafeHTMLSubstitution.render('plain text')).to.equal('plain text')
|
||||
})
|
||||
|
||||
it('should keep a simple string delimiter', function () {
|
||||
expect(SafeHTMLSubstitution.render("'")).to.equal(`'`)
|
||||
})
|
||||
|
||||
it('should escape double quotes', function () {
|
||||
expect(SafeHTMLSubstitution.render('"')).to.equal(`"`)
|
||||
})
|
||||
|
||||
it('should escape &', function () {
|
||||
expect(SafeHTMLSubstitution.render('&')).to.equal(`&`)
|
||||
})
|
||||
|
||||
it('should escape <', function () {
|
||||
expect(SafeHTMLSubstitution.render('<')).to.equal(`<`)
|
||||
})
|
||||
|
||||
it('should escape >', function () {
|
||||
expect(SafeHTMLSubstitution.render('>')).to.equal(`>`)
|
||||
})
|
||||
|
||||
it('should escape html', function () {
|
||||
expect(SafeHTMLSubstitution.render('<b>bad</b>')).to.equal(
|
||||
'<b>bad</b>'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('escape around substitutions', function () {
|
||||
it('should escape text inside a component', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0><i>inner</i></0>', [{ name: 'b' }])
|
||||
).to.equal('<b><i>inner</i></b>')
|
||||
})
|
||||
|
||||
it('should escape text in front of a component', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<i>PRE</i><0>inner</0>', [{ name: 'b' }])
|
||||
).to.equal('<i>PRE</i><b>inner</b>')
|
||||
})
|
||||
|
||||
it('should escape text after of a component', function () {
|
||||
expect(
|
||||
SafeHTMLSubstitution.render('<0>inner</0><i>POST</i>', [
|
||||
{ name: 'b' },
|
||||
])
|
||||
).to.equal('<b>inner</b><i>POST</i>')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
44
services/web/test/unit/src/HelperFiles/UrlHelperTests.js
Normal file
44
services/web/test/unit/src/HelperFiles/UrlHelperTests.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const { expect } = require('chai')
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const modulePath = require('path').join(
|
||||
__dirname,
|
||||
'../../../../app/src/Features/Helpers/UrlHelper.js'
|
||||
)
|
||||
|
||||
describe('UrlHelper', function () {
|
||||
beforeEach(function () {
|
||||
this.settings = {
|
||||
apis: { linkedUrlProxy: { url: undefined } },
|
||||
siteUrl: 'http://127.0.0.1:3000',
|
||||
}
|
||||
this.UrlHelper = SandboxedModule.require(modulePath, {
|
||||
requires: { '@overleaf/settings': this.settings },
|
||||
})
|
||||
})
|
||||
describe('getSafeRedirectPath', function () {
|
||||
it('sanitize redirect path to prevent open redirects', function () {
|
||||
expect(this.UrlHelper.getSafeRedirectPath('https://evil.com')).to.be
|
||||
.undefined
|
||||
|
||||
expect(this.UrlHelper.getSafeRedirectPath('//evil.com')).to.be.undefined
|
||||
|
||||
expect(this.UrlHelper.getSafeRedirectPath('//ol.com/evil')).to.equal(
|
||||
'/evil'
|
||||
)
|
||||
|
||||
expect(this.UrlHelper.getSafeRedirectPath('////evil.com')).to.be.undefined
|
||||
|
||||
expect(this.UrlHelper.getSafeRedirectPath('%2F%2Fevil.com')).to.equal(
|
||||
'/%2F%2Fevil.com'
|
||||
)
|
||||
|
||||
expect(
|
||||
this.UrlHelper.getSafeRedirectPath('http://foo.com//evil.com/bad')
|
||||
).to.equal('/evil.com/bad')
|
||||
|
||||
return expect(this.UrlHelper.getSafeRedirectPath('.evil.com')).to.equal(
|
||||
'/.evil.com'
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user