001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.rest.client; 019 020import static org.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertTrue; 022import static org.junit.jupiter.api.Assertions.fail; 023import static org.mockito.Mockito.mock; 024import static org.mockito.Mockito.when; 025 026import java.io.IOException; 027import javax.xml.bind.UnmarshalException; 028import org.apache.hadoop.hbase.HBaseConfiguration; 029import org.apache.hadoop.hbase.rest.Constants; 030import org.apache.hadoop.hbase.rest.model.StorageClusterVersionModel; 031import org.apache.hadoop.hbase.testclassification.SmallTests; 032import org.apache.hadoop.hbase.util.Bytes; 033import org.apache.hadoop.util.StringUtils; 034import org.junit.jupiter.api.Tag; 035import org.junit.jupiter.api.Test; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * Test class for {@link RemoteAdmin} to verify XML is parsed in a certain manner. 041 */ 042@Tag(SmallTests.TAG) 043public class TestXmlParsing { 044 045 private static final Logger LOG = LoggerFactory.getLogger(TestXmlParsing.class); 046 047 @Test 048 public void testParsingClusterVersion() throws Exception { 049 final String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" 050 + "<ClusterVersion Version=\"2.0.0\"/>"; 051 Client client = mock(Client.class); 052 RemoteAdmin admin = new RemoteAdmin(client, HBaseConfiguration.create(), null); 053 Response resp = new Response(200, null, Bytes.toBytes(xml)); 054 055 when(client.get("/version/cluster", Constants.MIMETYPE_XML)).thenReturn(resp); 056 057 StorageClusterVersionModel cv = admin.getClusterVersion(); 058 assertEquals("2.0.0", cv.getVersion()); 059 } 060 061 @Test 062 public void testFailOnExternalEntities() throws Exception { 063 final String externalEntitiesXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 064 + " <!DOCTYPE foo [ <!ENTITY xxe SYSTEM \"/tmp/foo\"> ] >" 065 + " <ClusterVersion>&xee;</ClusterVersion>"; 066 Client client = mock(Client.class); 067 RemoteAdmin admin = new RemoteAdmin(client, HBaseConfiguration.create(), null); 068 Response resp = new Response(200, null, Bytes.toBytes(externalEntitiesXml)); 069 070 when(client.get("/version/cluster", Constants.MIMETYPE_XML)).thenReturn(resp); 071 072 try { 073 admin.getClusterVersion(); 074 fail("Expected getClusterVersion() to throw an exception"); 075 } catch (IOException e) { 076 assertEquals(UnmarshalException.class, e.getCause().getClass(), 077 "Cause of exception ought to be a failure to parse the stream due to our " 078 + "invalid external entity. Make sure this isn't just a false positive due to " 079 + "implementation. see HBASE-19020."); 080 final String exceptionText = StringUtils.stringifyException(e); 081 final String expectedText = "\"xee\""; 082 LOG.debug("exception text: '{}'", exceptionText, e); 083 assertTrue(exceptionText.contains(expectedText), "Exception does not contain expected text"); 084 } 085 } 086}