Просмотр исходного кода

When serving output files, intelligently determine the appropriate content-type.

cherry pick 6fa3fda3ed28239cf3ac9720629f9707663aa197 from datajoy.
Shane Kilkelly 11 лет назад
Родитель
Сommit
3217b1d58f

+ 2 - 5
services/clsi/app.coffee

@@ -3,6 +3,7 @@ Settings = require "settings-sharelatex"
 logger = require "logger-sharelatex"
 logger.initialize("clsi")
 smokeTest = require "smoke-test-sharelatex"
+ContentTypeMapper = require "./app/js/ContentTypeMapper"
 
 Path = require "path"
 fs = require "fs"
@@ -46,17 +47,13 @@ ForbidSymlinks = require "./app/js/StaticServerForbidSymlinks"
 # and serving the files
 staticServer = ForbidSymlinks express.static, Settings.path.compilesDir, setHeaders: (res, path, stat) ->
 	if Path.basename(path) == "output.pdf"
-		res.set("Content-Type", "application/pdf")
 		# Calculate an etag in the same way as nginx
 		# https://github.com/tj/send/issues/65
 		etag = (path, stat) ->
 			'"' + Math.ceil(+stat.mtime / 1000).toString(16) +
 			'-' + Number(stat.size).toString(16) + '"'
 		res.set("Etag", etag(path, stat))
-	else
-		# Force plain treatment of other file types to prevent hosting of HTTP/JS files
-		# that could be used in same-origin/XSS attacks.
-		res.set("Content-Type", "text/plain")
+	res.set("Content-Type", ContentTypeMapper.map(path))
 
 app.get "/project/:project_id/output/*", (req, res, next) ->
 	if req.query?.build? && req.query.build.match(OutputCacheManager.BUILD_REGEX)

+ 26 - 0
services/clsi/app/coffee/ContentTypeMapper.coffee

@@ -0,0 +1,26 @@
+Path = require 'path'
+
+# here we coerce html, css and js to text/plain,
+# otherwise choose correct mime type based on file extension,
+# falling back to octet-stream
+module.exports = ContentTypeMapper =
+	map: (path) ->
+		switch Path.extname(path)
+			when '.txt', '.html', '.js', '.css'
+				return 'text/plain'
+			when '.csv'
+				return 'text/csv'
+			when '.pdf'
+				return 'application/pdf'
+			when '.png'
+				return 'image/png'
+			when '.jpg', '.jpeg'
+				return 'image/jpeg'
+			when '.tiff'
+				return 'image/tiff'
+			when '.gif'
+				return 'image/gif'
+			when '.svg'
+				return 'image/svg+xml'
+			else
+				return 'application/octet-stream'

+ 51 - 0
services/clsi/test/unit/coffee/ContentTypeMapperTests.coffee

@@ -0,0 +1,51 @@
+SandboxedModule = require('sandboxed-module')
+sinon = require('sinon')
+require('chai').should()
+modulePath = require('path').join __dirname, '../../../app/js/ContentTypeMapper'
+
+describe 'ContentTypeMapper', ->
+
+	beforeEach ->
+		@ContentTypeMapper = SandboxedModule.require modulePath
+
+	describe 'map', ->
+
+		it 'should map .txt to text/plain', ->
+			content_type = @ContentTypeMapper.map('example.txt')
+			content_type.should.equal 'text/plain'
+
+		it 'should map .csv to text/csv', ->
+			content_type = @ContentTypeMapper.map('example.csv')
+			content_type.should.equal 'text/csv'
+
+		it 'should map .pdf to application/pdf', ->
+			content_type = @ContentTypeMapper.map('example.pdf')
+			content_type.should.equal 'application/pdf'
+
+		it 'should fall back to octet-stream', ->
+			content_type = @ContentTypeMapper.map('example.unknown')
+			content_type.should.equal 'application/octet-stream'
+
+		describe 'coercing web files to plain text', ->
+
+			it 'should map .js to plain text', ->
+				content_type = @ContentTypeMapper.map('example.js')
+				content_type.should.equal 'text/plain'
+
+			it 'should map .html to plain text', ->
+				content_type = @ContentTypeMapper.map('example.html')
+				content_type.should.equal 'text/plain'
+
+			it 'should map .css to plain text', ->
+				content_type = @ContentTypeMapper.map('example.css')
+				content_type.should.equal 'text/plain'
+
+		describe 'image files', ->
+
+			it 'should map .png to image/png', ->
+				content_type = @ContentTypeMapper.map('example.png')
+				content_type.should.equal 'image/png'
+
+			it 'should map .jpeg to image/jpeg', ->
+				content_type = @ContentTypeMapper.map('example.jpeg')
+				content_type.should.equal 'image/jpeg'