99import com .thoughtworks .xstream .XStream ;
1010import com .thoughtworks .xstream .annotations .XStreamAlias ;
1111import freemarker .template .ObjectWrapper ;
12- import java .io .BufferedInputStream ;
1312import java .io .IOException ;
14- import java .io .InputStream ;
1513import java .io .UnsupportedEncodingException ;
16- import java .net .URLConnection ;
1714import java .net .URLDecoder ;
1815import java .text .DateFormat ;
1916import java .text .SimpleDateFormat ;
2320import java .util .Date ;
2421import java .util .List ;
2522import java .util .Locale ;
23+ import java .util .Optional ;
2624import java .util .TimeZone ;
2725import java .util .logging .Level ;
2826import java .util .logging .Logger ;
5351import org .springframework .beans .factory .annotation .Qualifier ;
5452import org .springframework .context .annotation .Bean ;
5553import org .springframework .context .annotation .Configuration ;
54+ import org .springframework .http .ContentDisposition ;
5655import org .springframework .http .HttpHeaders ;
5756import org .springframework .http .HttpStatus ;
5857import org .springframework .http .MediaType ;
@@ -129,24 +128,18 @@ protected String getTemplateName(Object object) {
129128 * @return Content type requested
130129 */
131130 protected static MediaType getMediaType (Resource resource , HttpServletRequest request ) {
132- if (resource .getType () == Resource .Type .DIRECTORY ) {
133- return getFormat (request );
134- } else if (resource .getType () == Resource .Type .RESOURCE ) {
135- String mimeType = URLConnection .guessContentTypeFromName (resource .name ());
136- if (mimeType == null
137- || MediaType .APPLICATION_OCTET_STREAM .toString ().equals (mimeType )) {
138- // try guessing from data
139- try (InputStream is = new BufferedInputStream (resource .in ())) {
140- mimeType = URLConnection .guessContentTypeFromStream (is );
141- } catch (IOException e ) {
142- // do nothing, we'll just use application/octet-stream
143- }
144- }
145- return mimeType == null
146- ? MediaType .APPLICATION_OCTET_STREAM
147- : MediaType .valueOf (mimeType );
148- } else {
149- return null ;
131+ switch (resource .getType ()) {
132+ case DIRECTORY :
133+ return getFormat (request );
134+ case RESOURCE :
135+ // set the mime if known by the servlet container, otherwise default to
136+ // application/octet-stream to mitigate potential cross-site scripting
137+ return Optional .ofNullable (request .getServletContext ())
138+ .map (sc -> sc .getMimeType (resource .name ()))
139+ .map (MediaType ::valueOf )
140+ .orElse (MediaType .APPLICATION_OCTET_STREAM );
141+ default :
142+ throw new ResourceNotFoundException ("Undefined resource path." );
150143 }
151144 }
152145
@@ -265,21 +258,27 @@ public Object resourceGet(
265258 Resource resource = resource (request );
266259 Operation operation = operation (operationName );
267260 Object result ;
268- response .setContentType (getFormat (format ).toString ());
269261
270262 if (operation == Operation .METADATA ) {
271263 result =
272264 wrapObject (
273265 new ResourceMetadataInfo (resource , request ),
274266 ResourceMetadataInfo .class );
267+ response .setContentType (getFormat (format ).toString ());
275268 } else {
276269 if (resource .getType () == Resource .Type .UNDEFINED ) {
277270 throw new ResourceNotFoundException ("Undefined resource path." );
278271 } else {
279272 HttpHeaders responseHeaders = new HttpHeaders ();
280273 MediaType mediaType = getMediaType (resource , request );
281274 responseHeaders .setContentType (mediaType );
282- response .setContentType (mediaType .toString ());
275+ if (resource .getType () == Resource .Type .RESOURCE ) {
276+ // Use Content-Disposition: attachment to mitigate potential XSS issues
277+ responseHeaders .setContentDisposition (
278+ ContentDisposition .builder ("attachment" )
279+ .filename (resource .name ())
280+ .build ());
281+ }
283282
284283 if (request .getMethod ().equals ("HEAD" )) {
285284 result = new ResponseEntity <>("" , responseHeaders , HttpStatus .OK );
@@ -288,6 +287,7 @@ public Object resourceGet(
288287 wrapObject (
289288 new ResourceDirectoryInfo (resource , request ),
290289 ResourceDirectoryInfo .class );
290+ response .setContentType (mediaType .toString ());
291291 } else {
292292 result = new ResponseEntity <>(resource .in (), responseHeaders , HttpStatus .OK );
293293 }
0 commit comments