Skip to content

Commit e9aa16d

Browse files
committed
Harden Zip CLI input validation
1 parent abd8055 commit e9aa16d

2 files changed

Lines changed: 79 additions & 8 deletions

File tree

src/main/java/algorithms/sprint0/Zip.java

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88
import java.io.IOException;
99
import java.io.InputStreamReader;
1010
import java.io.OutputStreamWriter;
11+
import java.io.StringReader;
1112
import java.nio.charset.StandardCharsets;
1213
import java.util.ArrayList;
1314
import java.util.List;
1415

1516
import static algorithms.sprint0.Utils.printList;
16-
import static algorithms.sprint0.Utils.readList;
1717

1818
public class Zip {
1919

20+
private static final int MAX_LIST_SIZE = 100_000;
21+
private static final int MAX_INPUT_LINE_LENGTH = 1_200_001;
22+
2023
static List<Integer> zip(List<Integer> a, List<Integer> b, int n) {
2124
if (n < 0) {
2225
throw new IllegalArgumentException("n >= 0 required");
@@ -34,14 +37,49 @@ static List<Integer> zip(List<Integer> a, List<Integer> b, int n) {
3437
public static void main(String[] args) throws IOException {
3538
try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
3639
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out, StandardCharsets.UTF_8))) {
37-
String sizeLine = reader.readLine();
38-
if (sizeLine == null) {
39-
throw new EOFException("Missing list size");
40+
try {
41+
process(reader, writer);
42+
} catch (IllegalArgumentException | EOFException exception) {
43+
System.err.println("Invalid input: " + exception.getMessage());
44+
}
45+
}
46+
}
47+
48+
static void process(BufferedReader reader, BufferedWriter writer) throws IOException {
49+
String sizeLine = readBoundedLine(reader);
50+
if (sizeLine == null) {
51+
throw new EOFException("Missing list size");
52+
}
53+
int n = parseInt(sizeLine.trim());
54+
if (n < 0 || n > MAX_LIST_SIZE) {
55+
throw new IllegalArgumentException("List size must be between 0 and " + MAX_LIST_SIZE);
56+
}
57+
List<Integer> a = parseList(readBoundedLine(reader));
58+
List<Integer> b = parseList(readBoundedLine(reader));
59+
if (a.size() < n || b.size() < n) {
60+
throw new IllegalArgumentException("Each list must contain at least n integers");
61+
}
62+
printList(zip(a, b, n), writer);
63+
}
64+
65+
private static String readBoundedLine(BufferedReader reader) throws IOException {
66+
StringBuilder line = new StringBuilder();
67+
int character;
68+
while ((character = reader.read()) != -1 && character != '\n') {
69+
if (line.length() == MAX_INPUT_LINE_LENGTH) {
70+
throw new IllegalArgumentException("Input line is too long");
71+
}
72+
if (character != '\r') {
73+
line.append((char) character);
4074
}
41-
int n = parseInt(sizeLine.trim());
42-
List<Integer> a = readList(reader);
43-
List<Integer> b = readList(reader);
44-
printList(zip(a, b, n), writer);
4575
}
76+
return character == -1 && line.length() == 0 ? null : line.toString();
77+
}
78+
79+
private static List<Integer> parseList(String line) throws IOException {
80+
if (line == null) {
81+
throw new EOFException("Missing integer list");
82+
}
83+
return Utils.readList(new BufferedReader(new StringReader(line)));
4684
}
4785
}

src/test/java/algorithms/sprint0/ZipTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
import static org.junit.jupiter.api.Assertions.assertThrows;
55
import static org.junit.jupiter.api.Assertions.assertTrue;
66

7+
import java.io.BufferedReader;
8+
import java.io.BufferedWriter;
9+
import java.io.IOException;
10+
import java.io.StringReader;
11+
import java.io.StringWriter;
712
import java.util.Arrays;
813
import java.util.List;
914
import org.junit.jupiter.api.Tag;
@@ -48,4 +53,32 @@ void rejectsNullLists() {
4853
assertThrows(NullPointerException.class, () -> Zip.zip(null, List.of(2), 1));
4954
assertThrows(NullPointerException.class, () -> Zip.zip(List.of(1), null, 1));
5055
}
56+
57+
@Test
58+
void processAcceptsValidInput() throws IOException {
59+
StringWriter output = new StringWriter();
60+
BufferedWriter writer = new BufferedWriter(output);
61+
62+
Zip.process(new BufferedReader(new StringReader("3\n1 5 6\n7 8 9\n")), writer);
63+
writer.flush();
64+
65+
assertEquals("1 7 5 8 6 9 ", output.toString());
66+
}
67+
68+
@Test
69+
void processRejectsMissingOrShortLists() {
70+
assertThrows(IOException.class, () -> process("3\n1 2 3\n"));
71+
assertThrows(IllegalArgumentException.class, () -> process("3\n1 2\n4 5 6\n"));
72+
}
73+
74+
@Test
75+
void processRejectsUnboundedSizesAndLines() {
76+
assertThrows(IllegalArgumentException.class, () -> process("100001\n1\n2\n"));
77+
String oversizedLine = "1".repeat(1_200_002);
78+
assertThrows(IllegalArgumentException.class, () -> process("1\n" + oversizedLine + "\n2\n"));
79+
}
80+
81+
private static void process(String input) throws IOException {
82+
Zip.process(new BufferedReader(new StringReader(input)), new BufferedWriter(new StringWriter()));
83+
}
5184
}

0 commit comments

Comments
 (0)