Skip to content

Commit ec9ec0a

Browse files
committed
Prevent parsing overly deep Node values
This commit updates the IDL parser to detect overly nested Node values and throw a syntax exception when the stack gets too deep. This prevents stack overflows and guards against a pathalogical case.
1 parent 5b35b28 commit ec9ec0a

File tree

3 files changed

+40
-0
lines changed

3 files changed

+40
-0
lines changed

smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlModelParser.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@
7272

7373
final class IdlModelParser {
7474

75+
/** Only allow nesting up to 250 arrays/objects in node values. */
76+
private static final int MAX_NESTING_LEVEL = 250;
77+
7578
private static final String PUT_KEY = "put";
7679
private static final String CREATE_KEY = "create";
7780
private static final String READ_KEY = "read";
@@ -111,6 +114,7 @@ final class IdlModelParser {
111114
private String namespace;
112115
private String definedVersion;
113116
private TraitEntry pendingDocumentationComment;
117+
private int nestingLevel;
114118

115119
/** Map of shape aliases to their targets. */
116120
private final Map<String, ShapeId> useShapes = new HashMap<>();
@@ -910,4 +914,14 @@ void skip() {
910914
int position() {
911915
return position;
912916
}
917+
918+
void increaseNestingLevel() {
919+
if (++nestingLevel >= MAX_NESTING_LEVEL) {
920+
throw syntax("Node value nesting too deep");
921+
}
922+
}
923+
924+
void decreaseNestingLevel() {
925+
nestingLevel--;
926+
}
913927
}

smithy-model/src/main/java/software/amazon/smithy/model/loader/IdlNodeParser.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ static Node parseTextBlock(IdlModelParser parser) {
104104
}
105105

106106
static ObjectNode parseObjectNode(IdlModelParser parser) {
107+
parser.increaseNestingLevel();
107108
SourceLocation location = parser.currentLocation();
108109
Map<StringNode, Node> entries = new LinkedHashMap<>();
109110
parser.expect('{');
@@ -132,6 +133,7 @@ static ObjectNode parseObjectNode(IdlModelParser parser) {
132133
}
133134

134135
parser.expect('}');
136+
parser.decreaseNestingLevel();
135137
return new ObjectNode(entries, location);
136138
}
137139

@@ -144,6 +146,7 @@ static String parseNodeObjectKey(IdlModelParser parser) {
144146
}
145147

146148
private static ArrayNode parseArrayNode(IdlModelParser parser) {
149+
parser.increaseNestingLevel();
147150
SourceLocation location = parser.currentLocation();
148151
List<Node> items = new ArrayList<>();
149152
parser.expect('[');
@@ -166,6 +169,7 @@ private static ArrayNode parseArrayNode(IdlModelParser parser) {
166169
}
167170

168171
parser.expect(']');
172+
parser.decreaseNestingLevel();
169173
return new ArrayNode(items, location);
170174
}
171175
}

smithy-model/src/test/java/software/amazon/smithy/model/loader/IdlModelLoaderTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616
package software.amazon.smithy.model.loader;
1717

1818
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.hamcrest.Matchers.containsString;
1920
import static org.hamcrest.Matchers.equalTo;
2021
import static org.hamcrest.Matchers.not;
2122

2223
import java.util.Optional;
24+
import org.junit.jupiter.api.Assertions;
2325
import org.junit.jupiter.api.Test;
2426
import software.amazon.smithy.model.Model;
2527
import software.amazon.smithy.model.SourceLocation;
2628
import software.amazon.smithy.model.shapes.MemberShape;
2729
import software.amazon.smithy.model.shapes.ResourceShape;
2830
import software.amazon.smithy.model.shapes.Shape;
2931
import software.amazon.smithy.model.shapes.ShapeId;
32+
import software.amazon.smithy.model.validation.ValidatedResultException;
3033

3134
public class IdlModelLoaderTest {
3235
@Test
@@ -94,4 +97,23 @@ public void defersApplyTargetAndTrait() {
9497
assertThat(shape.findTrait(ShapeId.from("smithy.example#bar")), not(Optional.empty()));
9598
assertThat(shape.findTrait(ShapeId.from("smithy.example.b#baz")), not(Optional.empty()));
9699
}
100+
101+
@Test
102+
public void limitsRecursion() {
103+
StringBuilder nodeBuilder = new StringBuilder("metadata foo = ");
104+
for (int i = 0; i < 251; i++) {
105+
nodeBuilder.append('[');
106+
}
107+
nodeBuilder.append("true");
108+
for (int i = 0; i < 251; i++) {
109+
nodeBuilder.append(']');
110+
}
111+
nodeBuilder.append("\n");
112+
113+
ValidatedResultException e = Assertions.assertThrows(ValidatedResultException.class, () -> {
114+
Model.assembler().addUnparsedModel("/foo.smithy", nodeBuilder.toString()).assemble().unwrap();
115+
});
116+
117+
assertThat(e.getMessage(), containsString("Node value nesting too deep"));
118+
}
97119
}

0 commit comments

Comments
 (0)