// ProxyHandler throws INTERNAL_SERVER_ERRORS without isRecoverable() check. @Test public void testRecoverableRewritingException() throws Exception { String url = "http://example.org/mypage.html"; String domain = "example.org"; setupProxyRequestMock(domain, url, true, -1, null, null); String contentType = "text/html; charset=UTF-8"; HttpResponse resp = new HttpResponseBuilder() .setResponseString("Hello") .addHeader("Content-Type", contentType) .create(); expect(pipeline.execute((HttpRequest) EasyMock.anyObject())).andReturn(resp); replay(); final StringBuilder stringBuilder = new StringBuilder(""); ResponseRewriter rewriter = getResponseRewriterThatThrowsExceptions(stringBuilder); ResponseRewriterRegistry rewriterRegistry = new DefaultResponseRewriterRegistry(Arrays.<ResponseRewriter>asList(rewriter), null); ProxyHandler proxyHandler = new ProxyHandler(pipeline, rewriterRegistry, true); request.setReturnOriginalContentOnError(true); HttpResponse recorder = proxyHandler.fetch(request); verify(); // Ensure that original content is returned. assertEquals(recorder.getHeader("Content-Type"), contentType); assertEquals("Hello", recorder.getResponseAsString()); assertEquals("exceptionThrown", stringBuilder.toString()); }
private void expectMime(String expectedMime, String contentMime, String outputMime) throws Exception { String url = "http://example.org/file.img?" + Param.REWRITE_MIME_TYPE.getKey() + '=' + expectedMime; String domain = "example.org"; setupProxyRequestMock(domain, url, false, -1, expectedMime, null); HttpRequest req = new HttpRequest(Uri.parse(url)).setRewriteMimeType(expectedMime); HttpResponse resp = new HttpResponseBuilder() .setResponseString("Hello") .addHeader("Content-Type", contentMime) .create(); expect(pipeline.execute(req)).andReturn(resp); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); assertEquals(outputMime, response.getHeader("Content-Type")); reset(); }
@Test public void testInvalidHeaderDropped() throws Exception { String url = "http://example.org/mypage.html"; String domain = "example.org"; setupProxyRequestMock(domain, url, true, -1, null, null); HttpRequest req = new HttpRequest(Uri.parse(url)).setIgnoreCache(true); String contentType = "text/html; charset=UTF-8"; HttpResponse resp = new HttpResponseBuilder() .setResponseString("Hello") .addHeader("Content-Type", contentType) .addHeader("Content-Length", "200") // Disallowed header. .addHeader(":", "someDummyValue") // Invalid header name. .create(); expect(pipeline.execute(req)).andReturn(resp); replay(); HttpResponse recorder = proxyHandler.fetch(request); verify(); assertNull(recorder.getHeader(":")); assertNull(recorder.getHeader("Content-Length")); assertEquals(recorder.getHeader("Content-Type"), contentType); }
@Test public void testLockedDomainEmbed() throws Exception { setupNoArgsProxyRequestMock("www.example.com", URL_ONE); expectGetAndReturnData(URL_ONE, DATA_ONE.getBytes()); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); assertEquals(DATA_ONE, response.getResponseAsString()); assertTrue(rewriter.responseWasRewritten()); }
@Override public void fetch(HttpServletRequest request, HttpServletResponse response) throws IOException, GadgetException { if (request.getHeader("If-Modified-Since") != null) { response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); return; } String host = request.getHeader("Host"); if (!lockedDomainService.isSafeForOpenProxy(host)) { // Force embedded images and the like to their own domain to avoid XSS // in gadget domains. String msg = "Embed request for url " + getParameter(request, URL_PARAM, "") + " made to wrong domain " + host; logger.info(msg); throw new GadgetException(GadgetException.Code.INVALID_PARAMETER, msg); } HttpRequest rcr = buildHttpRequest(request); HttpResponse results = fetcher.fetch(rcr); if (contentRewriterRegistry != null) { results = contentRewriterRegistry.rewriteHttpResponse(rcr, results); } setResponseHeaders(request, response, results); for (Map.Entry<String, List<String>> entry : results.getHeaders().entrySet()) { String name = entry.getKey(); if (!DISALLOWED_RESPONSE_HEADERS.contains(name.toLowerCase())) { for (String value : entry.getValue()) { response.addHeader(name, value); } } } if (rcr.getRewriteMimeType() != null) { response.setContentType(rcr.getRewriteMimeType()); } if (results.getHttpStatusCode() != HttpResponse.SC_OK) { response.sendError(results.getHttpStatusCode()); } IOUtils.copy(results.getResponse(), response.getOutputStream()); }
@Test public void testOctetSetOnNullContentType() throws Exception { String url = "http://example.org/file.evil"; String domain = "example.org"; setupNoArgsProxyRequestMock(domain, url); expectGetAndReturnHeaders(url, Maps.<String, List<String>>newHashMap()); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); assertEquals("application/octet-stream", response.getHeader("Content-Type")); assertNotNull(response.getHeader("Content-Disposition")); assertTrue(rewriter.responseWasRewritten()); }
public static String getMimeType(HttpRequest request, HttpResponse original) { String mimeType = request.getRewriteMimeType(); if (mimeType == null) { mimeType = original.getHeader("Content-Type"); } return mimeType != null ? mimeType.toLowerCase() : null; }
/** {@inheritDoc} */ public HttpResponse fetch(HttpRequest request) { if (!whitelist.allows(request.getUri().toJavaUri())) { log.warn( "A request to " + request.getUri() + " has been denied. To allow requests to this URL add the application URL to your whitelist (http://confluence.atlassian.com/x/KQfCDQ )."); return new HttpResponseBuilder() .setHttpStatusCode(HttpResponse.SC_FORBIDDEN) .setHeader("Content-Type", "text/plain") .setResponseString( "Requests to " + request.getUri() + " are not allowed. See your administrator about configuring a whitelist entry for this destination (http://confluence.atlassian.com/x/KQfCDQ ).") .create(); } HttpCacheKey cacheKey = new HttpCacheKey(request); HttpResponse response = cache.getResponse(cacheKey, request); if (response != null) { return response; } try { HttpUriRequest hcRequest = newHcRequest(request); if (request.getPostBodyLength() > 0) { ((HttpEntityEnclosingRequest) hcRequest) .setEntity(new InputStreamEntity(request.getPostBody(), request.getPostBodyLength())); } org.apache.http.HttpResponse hcResponse; try { // first try with TLS, the most common SSL protocol hcResponse = newHttpClient("TLSv1", request).execute(hcRequest); } catch (SSLException e) { log.debug( "SSL Exception establishing connection with TLSv1 protocol. Falling back to SSLv3.", e); // if TLS failed, try with SSLv3 hcResponse = newHttpClient("SSLv3", request).execute(hcRequest); } response = makeResponse(hcResponse); return cache.addResponse(cacheKey, request, response); } catch (IOException e) { if (e instanceof java.net.SocketTimeoutException || e instanceof java.net.SocketException) { return HttpResponse.timeout(); } else { log.error("Unable to retrieve response", e); } return HttpResponse.error(); } }
private void assertNoContentDispositionForFlash(String contentType) throws Exception { // Some headers may be blacklisted. These are OK. String url = "http://example.org/file.evil"; String domain = "example.org"; Map<String, List<String>> headers = ImmutableMap.of("Content-Type", Arrays.asList(contentType)); setupNoArgsProxyRequestMock(domain, url); expectGetAndReturnHeaders(url, headers); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); assertEquals(contentType, response.getHeader("Content-Type")); assertNull(response.getHeader("Content-Disposition")); assertTrue(rewriter.responseWasRewritten()); }
@Test public void doPostHttpError() throws Exception { setupPost(); expect(fixture.httpFetcher.fetch(request)).andReturn(HttpResponse.notFound()); fixture.replay(); servlet.doGet(fixture.request, recorder); assertResponseOk(HttpResponse.SC_NOT_FOUND, ""); }
@Test public void testHttpRequestFillsParentAndContainer() throws Exception { setupNoArgsProxyRequestMock("www.example.com", URL_ONE); // HttpRequest req = new HttpRequest(Uri.parse(URL_ONE)); HttpResponse resp = new HttpResponseBuilder().setResponse(DATA_ONE.getBytes()).create(); Capture<HttpRequest> httpRequest = new Capture<HttpRequest>(); expect(pipeline.execute(capture(httpRequest))).andReturn(resp); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); // Check that the HttpRequest passed in has all the relevant fields sets assertEquals("default", httpRequest.getValue().getContainer()); assertEquals(Uri.parse(URL_ONE), httpRequest.getValue().getUri()); assertEquals(DATA_ONE, response.getResponseAsString()); assertTrue(rewriter.responseWasRewritten()); }
/** * We don't actually rewrite the image we just ensure that it is in fact a valid and known image * type. */ private boolean rewriteProxiedImage( HttpRequest request, HttpResponse resp, MutableContent content) { boolean imageIsSafe = false; try { String contentType = resp.getHeader("Content-Type"); if (contentType == null || contentType.toLowerCase().startsWith("image/")) { // Unspecified or unknown image mime type. try { ImageFormat imageFormat = Sanselan.guessFormat( new ByteSourceInputStream(resp.getResponse(), request.getUri().getPath())); if (imageFormat == ImageFormat.IMAGE_FORMAT_UNKNOWN) { logger.log( Level.INFO, "Unable to sanitize unknown image type " + request.getUri().toString()); return true; } imageIsSafe = true; // Return false to indicate that no rewriting occurred return false; } catch (IOException ioe) { throw new RuntimeException(ioe); } catch (ImageReadException ire) { // Unable to read the image so its not safe logger.log( Level.INFO, "Unable to detect image type for " + request.getUri().toString() + " for sanitized content", ire); return true; } } else { return true; } } finally { if (!imageIsSafe) { content.setContent(""); } } }
@Test public void testHeadersPreserved() throws Exception { // Some headers may be blacklisted. These are OK. String url = "http://example.org/file.evil"; String domain = "example.org"; String contentType = "text/evil; charset=UTF-8"; String magicGarbage = "fadfdfdfd"; Map<String, List<String>> headers = Maps.newHashMap(); headers.put("Content-Type", Arrays.asList(contentType)); headers.put("X-Magic-Garbage", Arrays.asList(magicGarbage)); setupNoArgsProxyRequestMock(domain, url); expectGetAndReturnHeaders(url, headers); replay(); HttpResponse response = proxyHandler.fetch(request); verify(); assertEquals(contentType, response.getHeader("Content-Type")); assertEquals(magicGarbage, response.getHeader("X-Magic-Garbage")); assertTrue(rewriter.responseWasRewritten()); }
@Test public void testGetFallback() throws Exception { String url = "http://example.org/file.evil"; String domain = "example.org"; String fallback_url = "http://fallback.com/fallback.png"; setupProxyRequestMock(domain, url, true, -1, null, fallback_url); HttpRequest req = new HttpRequest(Uri.parse(url)).setIgnoreCache(true); HttpResponse resp = HttpResponse.error(); HttpResponse fallback_resp = new HttpResponse("Fallback"); expect(pipeline.execute(req)).andReturn(resp); expect(pipeline.execute(isA(HttpRequest.class))).andReturn(fallback_resp); replay(); proxyHandler.fetch(request); verify(); }
/** Sanitize a CSS file. */ private boolean rewriteProxiedCss( HttpRequest request, HttpResponse response, MutableContent content, ContentRewriterFeature rewriterFeature) { String sanitized = ""; try { String contentType = response.getHeader("Content-Type"); if (contentType == null || contentType.toLowerCase().startsWith("text/")) { SanitizingProxyingLinkRewriter cssImportRewriter = sanitizingProxyingLinkRewriterFactory.create( request.getGadget(), rewriterFeature, request.getContainer(), "text/css", false, request.getIgnoreCache()); SanitizingProxyingLinkRewriter cssImageRewriter = sanitizingProxyingLinkRewriterFactory.create( request.getGadget(), rewriterFeature, request.getContainer(), "image/*", false, request.getIgnoreCache()); sanitized = cssSanitizer.sanitize( content.getContent(), request.getUri(), cssImportRewriter, cssImageRewriter); } return true; } finally { // Set sanitized content in finally to ensure it is always cleared in // the case of errors content.setContent(sanitized); } }