Skip to content

Commit 131ee43

Browse files
mprokopchuksureshanaparti
authored andcommitted
Escape snapshot names in libvirt XML to prevent XML injection
1 parent a8c8c18 commit 131ee43

4 files changed

Lines changed: 34 additions & 4 deletions

File tree

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreateVMSnapshotCommandWrapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
package com.cloud.hypervisor.kvm.resource.wrapper;
2121

22+
import org.apache.commons.lang.StringEscapeUtils;
2223
import org.libvirt.Connect;
2324
import org.libvirt.Domain;
2425
import org.libvirt.DomainInfo.DomainState;
@@ -57,7 +58,7 @@ public Answer execute(final CreateVMSnapshotCommand cmd, final LibvirtComputingR
5758
"Create Instance Snapshot Failed due to Instance is not running: " + vmName + " with domainState = " + domainState);
5859
}
5960

60-
String vmSnapshotXML = "<domainsnapshot>" + " <name>" + vmSnapshotName + "</name>"
61+
String vmSnapshotXML = "<domainsnapshot>" + " <name>" + StringEscapeUtils.escapeXml(vmSnapshotName) + "</name>"
6162
+ " <memory snapshot='internal' />" + "</domainsnapshot>";
6263

6364
dm.snapshotCreateXML(vmSnapshotXML);

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtManageSnapshotCommandWrapper.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.File;
2323
import java.text.MessageFormat;
2424

25+
import org.apache.commons.lang.StringEscapeUtils;
2526
import org.libvirt.Connect;
2627
import org.libvirt.Domain;
2728
import org.libvirt.DomainInfo.DomainState;
@@ -79,7 +80,7 @@ public Answer execute(final ManageSnapshotCommand command, final LibvirtComputin
7980
+ " <uuid>{1}</uuid>" + " </domain>" + " </domainsnapshot>");
8081

8182
final String vmUuid = vm.getUUIDString();
82-
final Object[] args = new Object[] {snapshotName, vmUuid};
83+
final Object[] args = new Object[] {StringEscapeUtils.escapeXml(snapshotName), vmUuid};
8384
final String snapshot = snapshotXML.format(args);
8485
logger.debug(snapshot);
8586
if (command.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUtilitiesHelper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
2727
import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
2828
import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
29+
import org.apache.commons.lang.StringEscapeUtils;
2930
import org.apache.logging.log4j.Logger;
3031
import org.apache.logging.log4j.LogManager;
3132
import org.libvirt.Connect;
@@ -112,9 +113,9 @@ public Script buildScript(final String scriptPath) {
112113
}
113114

114115
public String generateVMSnapshotXML(VMSnapshotTO snapshot, VMSnapshotTO parent, String domainXmlDesc) {
115-
String parentName = (parent == null)? "": (" <parent><name>" + parent.getSnapshotName() + "</name></parent>\n");
116+
String parentName = (parent == null)? "": (" <parent><name>" + StringEscapeUtils.escapeXml(parent.getSnapshotName()) + "</name></parent>\n");
116117
String vmSnapshotXML = "<domainsnapshot>\n"
117-
+ " <name>" + snapshot.getSnapshotName() + "</name>\n"
118+
+ " <name>" + StringEscapeUtils.escapeXml(snapshot.getSnapshotName()) + "</name>\n"
118119
+ " <state>running</state>\n"
119120
+ parentName
120121
+ " <creationTime>" + (int) Math.rint(snapshot.getCreateTime()/1000) + "</creationTime>\n"

plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtUtilitiesHelperTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.mockito.Mockito;
2626
import org.mockito.junit.MockitoJUnitRunner;
2727

28+
import com.cloud.agent.api.VMSnapshotTO;
2829
import com.cloud.utils.Pair;
2930

3031
import junit.framework.TestCase;
@@ -75,4 +76,30 @@ public void validateIsLibvirtVersionEqualOrHigherThanVersionInParameterLibvirtVe
7576
Assert.assertEquals(String.valueOf(libvirtVersion), result.first());
7677
Assert.assertTrue(result.second());
7778
}
79+
80+
@Test
81+
public void generateVMSnapshotXMLEscapesSnapshotName() {
82+
VMSnapshotTO snapshot = Mockito.mock(VMSnapshotTO.class);
83+
Mockito.doReturn("i-2-3-VM_VS_</name><disks><disk name='vda'/></disks><name>x").when(snapshot).getSnapshotName();
84+
Mockito.doReturn(1000L).when(snapshot).getCreateTime();
85+
86+
String xml = libvirtUtilitiesHelperSpy.generateVMSnapshotXML(snapshot, null, "<domain/>");
87+
88+
Assert.assertTrue(xml.contains("<name>i-2-3-VM_VS_&lt;/name&gt;&lt;disks&gt;&lt;disk name=&apos;vda&apos;/&gt;&lt;/disks&gt;&lt;name&gt;x</name>"));
89+
Assert.assertFalse(xml.contains("<disks>"));
90+
}
91+
92+
@Test
93+
public void generateVMSnapshotXMLEscapesParentSnapshotName() {
94+
VMSnapshotTO snapshot = Mockito.mock(VMSnapshotTO.class);
95+
Mockito.doReturn("child").when(snapshot).getSnapshotName();
96+
Mockito.doReturn(1000L).when(snapshot).getCreateTime();
97+
VMSnapshotTO parent = Mockito.mock(VMSnapshotTO.class);
98+
Mockito.doReturn("parent</name><disks/>").when(parent).getSnapshotName();
99+
100+
String xml = libvirtUtilitiesHelperSpy.generateVMSnapshotXML(snapshot, parent, "<domain/>");
101+
102+
Assert.assertTrue(xml.contains("<parent><name>parent&lt;/name&gt;&lt;disks/&gt;</name></parent>"));
103+
Assert.assertFalse(xml.contains("<disks/>"));
104+
}
78105
}

0 commit comments

Comments
 (0)