1
0
mirror of https://github.com/google/nomulus synced 2026-06-09 16:33:02 +00:00

Forbid mismatched inner element in XML flow inputs (#3104)

This commit is contained in:
gbrodman
2026-06-26 15:04:12 -04:00
committed by GitHub
parent 403c7ad275
commit 0ab612ab23
2 changed files with 118 additions and 0 deletions
@@ -14,6 +14,7 @@
package google.registry.flows.picker;
import com.google.common.base.Ascii;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableTable;
@@ -255,6 +256,17 @@ public class FlowPicker {
if (innerCommand == null && !(eppInput.getCommandWrapper() instanceof Hello)) {
throw new MissingCommandException();
}
if (innerCommand instanceof ResourceCommandWrapper resourceCommandWrapper) {
ResourceCommand resourceCommand = resourceCommandWrapper.getResourceCommand();
if (resourceCommand != null) {
String wrapperName = innerCommand.getClass().getSimpleName();
String commandName = resourceCommand.getClass().getSimpleName();
if (!wrapperName.equals(commandName)) {
throw new MismatchedCommandException(
Ascii.toLowerCase(wrapperName), Ascii.toLowerCase(commandName));
}
}
}
// Try the FlowProviders until we find a match. The order matters because it's possible to
// match multiple FlowProviders and so more specific matches are tried first.
for (FlowProvider flowProvider : FLOW_PROVIDERS) {
@@ -279,4 +291,14 @@ public class FlowPicker {
super("Command missing");
}
}
/** Command wrapper and inner resource command do not match. */
static class MismatchedCommandException extends SyntaxErrorException {
public MismatchedCommandException(String wrapperName, String commandName) {
super(
String.format(
"EPP command wrapper <%s> does not match resource command <%s>",
wrapperName, commandName));
}
}
}
@@ -0,0 +1,96 @@
// Copyright 2026 The Nomulus Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package google.registry.flows.picker;
import static com.google.common.truth.Truth.assertThat;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.jupiter.api.Assertions.assertThrows;
import google.registry.flows.Flow;
import google.registry.flows.domain.DomainCheckFlow;
import google.registry.flows.domain.DomainCreateFlow;
import google.registry.model.domain.DomainCommand;
import google.registry.model.eppcommon.EppXmlTransformer;
import google.registry.model.eppinput.EppInput;
import org.junit.jupiter.api.Test;
/** Unit tests for {@link FlowPicker}. */
class FlowPickerTest {
@Test
void testGetFlowClass_matchingWrapperAndCommand_returnsFlow() throws Exception {
EppInput checkEppInput = EppInput.create(EppInput.Check.create(new DomainCommand.Check()));
Class<? extends Flow> checkFlow = FlowPicker.getFlowClass(checkEppInput);
assertThat(checkFlow).isEqualTo(DomainCheckFlow.class);
EppInput createEppInput = EppInput.create(EppInput.Create.create(new DomainCommand.Create()));
Class<? extends Flow> createFlow = FlowPicker.getFlowClass(createEppInput);
assertThat(createFlow).isEqualTo(DomainCreateFlow.class);
}
@Test
void testGetFlowClass_mismatchedWrapperAndCommand_throwsSyntaxErrorException() {
EppInput mismatchedEppInput1 =
EppInput.create(EppInput.Check.create(new DomainCommand.Create()));
assertThat(
assertThrows(
FlowPicker.MismatchedCommandException.class,
() -> FlowPicker.getFlowClass(mismatchedEppInput1)))
.hasMessageThat()
.isEqualTo("EPP command wrapper <check> does not match resource command <create>");
EppInput mismatchedEppInput2 =
EppInput.create(EppInput.Create.create(new DomainCommand.Check()));
assertThat(
assertThrows(
FlowPicker.MismatchedCommandException.class,
() -> FlowPicker.getFlowClass(mismatchedEppInput2)))
.hasMessageThat()
.contains("EPP command wrapper <create> does not match resource command <check>");
}
@Test
void testGetFlowClass_mismatchedXml_throwsMismatchedCommandException() throws Exception {
String mismatchedXml =
"""
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<epp xmlns="urn:ietf:params:xml:ns:epp-1.0">
<command>
<check>
<domain:create xmlns:domain="urn:ietf:params:xml:ns:domain-1.0">
<domain:name>example.com</domain:name>
<domain:authInfo>
<domain:pw>fooBAR123</domain:pw>
</domain:authInfo>
</domain:create>
</check>
<clTRID>ABC-12345</clTRID>
</command>
</epp>
""";
// Verify JAXB successfully unmarshals the mismatched XML without throwing any errors
EppInput eppInput = EppXmlTransformer.unmarshal(EppInput.class, mismatchedXml.getBytes(UTF_8));
assertThat(eppInput).isNotNull();
// Verify that FlowPicker intercepts the unmarshalled input and blocks it
assertThat(
assertThrows(
FlowPicker.MismatchedCommandException.class,
() -> FlowPicker.getFlowClass(eppInput)))
.hasMessageThat()
.contains("EPP command wrapper <check> does not match resource command <create>");
}
}