Skip to content

Commit b79d503

Browse files
authored
Consolidate remaining validated robustness fixes (#116)
* Validate sprint8 CLI input * Validate sprint7 solver input bounds * Harden WaterWorld input dimensions * Validate DorogayaSet input bounds * Harden PyramidSort input bounds * Harden sprint4 CLI input handling * Validate deque capacity before allocation * Validate SleightOfHand input * Harden Zip CLI input validation * Reject overflowing digit recompositions * Fix Unicode case expansion in AdjustCase * Restore DigitalRoot legacy API compatibility * Prevent SquareDigit overflow crash * Handle overflow when reversing integers * Validate Roman numeral parser input * Validate resistor values before encoding * Address remaining validation review findings * fix: preserve valid search workloads
1 parent f1ea7c2 commit b79d503

36 files changed

Lines changed: 842 additions & 144 deletions

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

Lines changed: 50 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,53 @@ 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());
4044
}
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);
4545
}
4646
}
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' && character != '\r') {
69+
if (line.length() == MAX_INPUT_LINE_LENGTH) {
70+
throw new IllegalArgumentException("Input line is too long");
71+
}
72+
line.append((char) character);
73+
}
74+
if (character == '\r') {
75+
reader.mark(1);
76+
if (reader.read() != '\n') {
77+
reader.reset();
78+
}
79+
}
80+
return character == -1 && line.length() == 0 ? null : line.toString();
81+
}
82+
83+
private static List<Integer> parseList(String line) throws IOException {
84+
if (line == null) {
85+
throw new EOFException("Missing integer list");
86+
}
87+
return Utils.readList(new BufferedReader(new StringReader(line)));
88+
}
4789
}

src/main/java/algorithms/sprint1/SleightOfHand.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,21 @@ int nextInt() throws IOException {
4848
return val * sign;
4949
}
5050

51-
String next() throws IOException {
51+
String next(int maxLength) throws IOException {
5252
int c;
5353
do {
5454
c = read();
5555
if (c == -1) throw new EOFException("Unexpected EOF");
5656
} while (c <= ' ');
5757

58-
byte[] tmp = new byte[32];
58+
byte[] tmp = new byte[Math.min(32, maxLength)];
5959
int n = 0;
6060
while (c > ' ') {
61+
if (n == maxLength) {
62+
throw new IOException("Token length exceeds " + maxLength);
63+
}
6164
if (n == tmp.length) {
62-
byte[] t2 = new byte[tmp.length * 2];
65+
byte[] t2 = new byte[Math.min(maxLength, tmp.length * 2)];
6366
System.arraycopy(tmp, 0, t2, 0, tmp.length);
6467
tmp = t2;
6568
}
@@ -128,14 +131,17 @@ private static void run() throws Exception {
128131
int[] count = new int[10];
129132

130133
for (int r = 0; r < 4; r++) {
131-
StringBuilder row = new StringBuilder(in.next());
134+
StringBuilder row = new StringBuilder(in.next(4));
132135
// На всякий случай, если токенайзер разделит строку (обычно не будет)
133136
while (row.length() < 4) {
134-
row.append(in.next());
137+
row.append(in.next(4 - row.length()));
135138
}
136139
for (int c = 0; c < 4; c++) {
137140
char ch = row.charAt(c);
138141
if (ch != '.') {
142+
if (ch < '0' || ch > '9') {
143+
throw new IOException("Invalid grid cell: " + ch);
144+
}
139145
count[ch - '0']++;
140146
}
141147
}
@@ -219,7 +225,12 @@ static int solve(int k, int[][] a) {
219225

220226
for (int[] row : a) {
221227
for (int v : row) {
222-
if (v != 0) count[v]++;
228+
if (v != 0) {
229+
if (v < 0 || v > 9) {
230+
throw new IllegalArgumentException("Grid values must be between 0 and 9");
231+
}
232+
count[v]++;
233+
}
223234
}
224235
}
225236

src/main/java/algorithms/sprint2/Deque.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
public class Deque {
5656

5757
// -------------------- RING BUFFER DEQUE --------------------
58+
private static final int MAX_CAPACITY = 100_000;
59+
5860
static final class RingDeque {
5961
private final int[] a;
6062
private final int cap;
@@ -63,8 +65,8 @@ static final class RingDeque {
6365
private int size = 0;
6466

6567
RingDeque(int cap) {
66-
this.cap = cap;
67-
this.a = new int[cap];
68+
this.cap = validateCapacity(cap);
69+
this.a = new int[this.cap];
6870
}
6971

7072
private int next(int i) {
@@ -112,9 +114,19 @@ int popBack() {
112114
}
113115
}
114116

117+
private static int validateCapacity(int cap) {
118+
if (cap < 0 || cap > MAX_CAPACITY) {
119+
throw new IllegalArgumentException("Deque capacity is out of range");
120+
}
121+
return cap;
122+
}
123+
115124
private static void process(FastIn in, FastOut out) throws Exception {
116125
int n = in.nextInt();
117126
int m = in.nextInt();
127+
if (n < 0 || n > MAX_CAPACITY) {
128+
throw new IllegalArgumentException("Command count is out of range");
129+
}
118130

119131
RingDeque dq = new RingDeque(m);
120132

@@ -235,6 +247,10 @@ private static void test() throws Exception {
235247
)
236248
);
237249

250+
// Некорректная емкость отклоняется, а не меняет заявленную семантику дека.
251+
assertRejected("2\n-1\npush_back 1\npop_front\n");
252+
assertRejected("1\n1000000000\npop_front\n");
253+
238254
// Wrap-around: head/tail должны корректно "перепрыгивать" границу массива
239255
assertEq(
240256
"1\n4\n2\n3\n",
@@ -261,6 +277,15 @@ static void assertEq(String exp, String act) {
261277
}
262278
}
263279

280+
private static void assertRejected(String input) throws Exception {
281+
try {
282+
solveIO(input);
283+
throw new AssertionError("Expected invalid deque capacity to be rejected");
284+
} catch (IllegalArgumentException expected) {
285+
// Expected validation failure.
286+
}
287+
}
288+
264289
public static void main(String[] args) throws Exception {
265290
if (System.getProperty("os.name").startsWith("Windows")) {
266291
test();

src/main/java/algorithms/sprint4/FindSystem.java

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@
99
import java.util.HashSet;
1010
import java.util.Map;
1111
import java.util.StringTokenizer;
12+
import java.io.BufferedWriter;
13+
import java.io.OutputStreamWriter;
14+
import java.nio.charset.StandardCharsets;
1215

1316
// https://contest.yandex.ru/contest/24414/run-report/160043341/
1417

1518
class FindSystem {
1619

20+
private static final int MAX_DOCUMENTS = 10_000;
21+
private static final int MAX_QUERIES = 10_000;
22+
private static final int MAX_LINE_LENGTH = 10_000;
23+
1724
/*
1825
* Принцип работы алгоритма:
1926
* 1) Строим обратный индекс:
@@ -141,23 +148,25 @@ private static boolean isBetter(int docId1, int score1, int docId2, int score2)
141148
private static void solve() throws Exception {
142149
FastReader reader = new FastReader(System.in);
143150

144-
int n = reader.nextInt();
151+
int n = reader.nextInt(MAX_DOCUMENTS);
145152
String[] docs = new String[n];
146153
for (int i = 0; i < n; i++) {
147-
docs[i] = reader.nextLine();
154+
docs[i] = reader.nextLine(MAX_LINE_LENGTH);
148155
}
149156

150157
HashMap<String, ArrayList<int[]>> index = buildIndex(docs);
151158

152-
int m = reader.nextInt();
153-
StringBuilder out = new StringBuilder();
159+
int m = reader.nextInt(MAX_QUERIES);
160+
BufferedWriter out = new BufferedWriter(
161+
new OutputStreamWriter(System.out, StandardCharsets.UTF_8));
154162

155163
for (int i = 0; i < m; i++) {
156-
String query = reader.nextLine();
157-
out.append(processQuery(query, index)).append('\n');
164+
String query = reader.nextLine(MAX_LINE_LENGTH);
165+
out.write(processQuery(query, index));
166+
out.newLine();
158167
}
159168

160-
System.out.print(out);
169+
out.flush();
161170
}
162171

163172
private static void test() {
@@ -229,7 +238,7 @@ private int read() throws IOException {
229238
return buffer[ptr++];
230239
}
231240

232-
int nextInt() throws IOException {
241+
int nextInt(int max) throws IOException {
233242
int c;
234243
do {
235244
c = read();
@@ -238,15 +247,21 @@ int nextInt() throws IOException {
238247
}
239248
} while (c <= ' ');
240249

241-
int value = 0;
250+
long value = 0;
242251
while (c > ' ') {
252+
if (c < '0' || c > '9') {
253+
throw new IOException("Expected a non-negative integer");
254+
}
243255
value = value * 10 + c - '0';
256+
if (value > max) {
257+
throw new IOException("Input value exceeds limit");
258+
}
244259
c = read();
245260
}
246-
return value;
261+
return (int) value;
247262
}
248263

249-
String nextLine() throws IOException {
264+
String nextLine(int maxLength) throws IOException {
250265
int c = read();
251266

252267
while (c == '\n' || c == '\r') {
@@ -255,6 +270,9 @@ String nextLine() throws IOException {
255270

256271
StringBuilder sb = new StringBuilder();
257272
while (c != -1 && c != '\n' && c != '\r') {
273+
if (sb.length() == maxLength) {
274+
throw new IOException("Input line exceeds limit");
275+
}
258276
sb.append((char) c);
259277
c = read();
260278
}

0 commit comments

Comments
 (0)