| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- 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)
- })
- })
- })
- })
|