mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-01 05:11:34 +02:00
271 lines
7.0 KiB
JavaScript
271 lines
7.0 KiB
JavaScript
import { vi, describe, beforeEach, it } from 'vitest'
|
|
|
|
import path from 'node:path'
|
|
import sinon from 'sinon'
|
|
const modulePath = path.join(
|
|
import.meta.dirname,
|
|
'../../../app/js/StaticServerForbidSymlinks'
|
|
)
|
|
|
|
describe('StaticServerForbidSymlinks', function () {
|
|
beforeEach(async function (ctx) {
|
|
ctx.settings = {
|
|
path: {
|
|
compilesDir: '/compiles/here',
|
|
},
|
|
}
|
|
|
|
ctx.fs = {}
|
|
|
|
vi.doMock('@overleaf/settings', () => ({
|
|
default: ctx.settings,
|
|
}))
|
|
|
|
vi.doMock('fs', () => ({
|
|
default: ctx.fs,
|
|
}))
|
|
|
|
ctx.ForbidSymlinks = (await import(modulePath)).default
|
|
|
|
ctx.dummyStatic = (rootDir, options) => (req, res, next) =>
|
|
// console.log "dummyStatic serving file", rootDir, "called with", req.url
|
|
// serve it
|
|
next()
|
|
|
|
ctx.StaticServerForbidSymlinks = ctx.ForbidSymlinks(
|
|
ctx.dummyStatic,
|
|
ctx.settings.path.compilesDir
|
|
)
|
|
ctx.req = {
|
|
params: {
|
|
project_id: '12345',
|
|
},
|
|
}
|
|
|
|
ctx.res = {}
|
|
ctx.req.url = '/12345/output.pdf'
|
|
})
|
|
|
|
describe('sending a normal file through', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.fs.realpath = sinon
|
|
.stub()
|
|
.callsArgWith(
|
|
1,
|
|
null,
|
|
`${ctx.settings.path.compilesDir}/${ctx.req.params.project_id}/output.pdf`
|
|
)
|
|
})
|
|
|
|
it('should call next', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(200)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res, err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a missing file', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.fs.realpath = sinon
|
|
.stub()
|
|
.callsArgWith(
|
|
1,
|
|
{ code: 'ENOENT' },
|
|
`${ctx.settings.path.compilesDir}/${ctx.req.params.project_id}/unknown.pdf`
|
|
)
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a new line', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/12345/output.pdf\nother file'
|
|
ctx.fs.realpath = sinon.stub().yields()
|
|
})
|
|
|
|
it('should process the correct file', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = () => {
|
|
ctx.fs.realpath.should.have.been.calledWith(
|
|
`${ctx.settings.path.compilesDir}/12345/output.pdf\nother file`
|
|
)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a symlink file', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.fs.realpath = sinon
|
|
.stub()
|
|
.callsArgWith(1, null, `/etc/${ctx.req.params.project_id}/output.pdf`)
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a relative file', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/12345/../67890/output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a unnormalized file containing .', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/12345/foo/./output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a file containing an empty path', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/12345/foo//output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a non-project file', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/.foo/output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a file outside the compiledir', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/../bar/output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a file with no leading /', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = './../bar/output.pdf'
|
|
})
|
|
|
|
it('should send a 404', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(404)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with a github style path', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.req.url = '/henryoswald-latex_example/output/output.log'
|
|
ctx.fs.realpath = sinon
|
|
.stub()
|
|
.callsArgWith(
|
|
1,
|
|
null,
|
|
`${ctx.settings.path.compilesDir}/henryoswald-latex_example/output/output.log`
|
|
)
|
|
})
|
|
|
|
it('should call next', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(200)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res, err => {
|
|
if (err) reject(err)
|
|
resolve()
|
|
})
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('with an error from fs.realpath', function () {
|
|
beforeEach(function (ctx) {
|
|
ctx.fs.realpath = sinon.stub().callsArgWith(1, 'error')
|
|
})
|
|
|
|
it('should send a 500', async function (ctx) {
|
|
await new Promise((resolve, reject) => {
|
|
ctx.res.sendStatus = function (resCode) {
|
|
resCode.should.equal(500)
|
|
resolve()
|
|
}
|
|
ctx.StaticServerForbidSymlinks(ctx.req, ctx.res)
|
|
})
|
|
})
|
|
})
|
|
})
|