Skip to content

Commit fb74077

Browse files
committed
fix: validate CLI numeric limits
Motivation: sjsonnet accepted invalid numeric CLI limits for stack frames, trace cropping, and parser recursion depth. Non-positive stack limits created a broken runtime setting, negative trace limits behaved like an undocumented full-trace setting, and negative parser recursion depth reached the parser as Parsing exceeded maximum recursion depth of -1. Modification: Validate --max-stack >= 1, --max-trace >= 0, and --max-parser-recursion-depth >= 0 after CLI parsing and before constructing Settings. Add JVM CLI regression tests for invalid stack, trace, and parser-depth values, and preserve the boundary behavior that --max-trace 0 keeps a full trace and --max-parser-recursion-depth 0 still allows non-recursive expressions. Result: Invalid numeric CLI limits now fail before evaluation or parsing with user-facing CLI errors. Stack and trace behavior matches C++ jsonnet and go-jsonnet for shared flags, while the sjsonnet-specific parser-depth flag now rejects only the invalid negative range. References: C++ jsonnet cmd/jsonnet.cpp rejects --max-stack < 1 and --max-trace < 0. go-jsonnet cmd/jsonnet/cmd.go rejects --max-stack < 1 and --max-trace < 0. sjsonnet readme.md documents --max-parser-recursion-depth without a negative-value meaning.
1 parent e686d89 commit fb74077

2 files changed

Lines changed: 56 additions & 0 deletions

File tree

sjsonnet/src-jvm-native/sjsonnet/SjsonnetMainBase.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,21 @@ object SjsonnetMainBase {
195195
Left("ERROR: cannot use --no-trailing-newline with --yaml-stream")
196196
else Right(())
197197
}
198+
_ <- {
199+
if (config.maxStack < 1) Left(s"ERROR: invalid --max-stack value: ${config.maxStack}")
200+
else Right(())
201+
}
202+
_ <- {
203+
if (config.maxTrace < 0) Left(s"ERROR: invalid --max-trace value: ${config.maxTrace}")
204+
else Right(())
205+
}
206+
_ <- {
207+
if (config.maxParserRecursionDepth < 0)
208+
Left(
209+
s"ERROR: invalid --max-parser-recursion-depth value: ${config.maxParserRecursionDepth}"
210+
)
211+
else Right(())
212+
}
198213
file <- Right(config.file)
199214
debugStats =
200215
if (config.debugStats.value) { val s = new DebugStats; statsToReport = s; s }

sjsonnet/test/src-jvm/sjsonnet/MainTests.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,47 @@ object MainTests extends TestSuite {
147147
assert(err.contains("Max stack frames exceeded."))
148148
}
149149

150+
test("maxStackRejectsNonPositiveValues") {
151+
val (zeroRes, zeroOut, zeroErr) = runMain("--exec", "1", "--max-stack", "0")
152+
assert(zeroRes == 1)
153+
assert(zeroOut.isEmpty)
154+
assert(zeroErr.contains("ERROR: invalid --max-stack value: 0"))
155+
assert(!zeroErr.contains("Max stack frames exceeded."))
156+
157+
val (negativeRes, negativeOut, negativeErr) = runMain("--exec", "1", "--max-stack", "-1")
158+
assert(negativeRes == 1)
159+
assert(negativeOut.isEmpty)
160+
assert(negativeErr.contains("ERROR: invalid --max-stack value: -1"))
161+
assert(!negativeErr.contains("Max stack frames exceeded."))
162+
}
163+
164+
test("maxTraceRejectsNegativeValues") {
165+
val (res, out, err) = runMain("--exec", "error 'x'", "--max-trace", "-1")
166+
assert(res == 1)
167+
assert(out.isEmpty)
168+
assert(err.contains("ERROR: invalid --max-trace value: -1"))
169+
assert(!err.contains("sjsonnet.Error"))
170+
}
171+
172+
test("maxParserRecursionDepthRejectsNegativeValues") {
173+
val (res, out, err) = runMain("--exec", "1", "--max-parser-recursion-depth", "-1")
174+
assert(res == 1)
175+
assert(out.isEmpty)
176+
assert(err.contains("ERROR: invalid --max-parser-recursion-depth value: -1"))
177+
assert(!err.contains("Parsing exceeded maximum recursion depth of -1"))
178+
}
179+
180+
test("maxParserRecursionDepthZeroAllowsNonRecursiveExpressions") {
181+
val (res, out, err) = runMain("--exec", "1", "--max-parser-recursion-depth", "0")
182+
assert((res, out, err) == ((0, "1\n", "")))
183+
184+
val (nestedRes, nestedOut, nestedErr) =
185+
runMain("--exec", "[1]", "--max-parser-recursion-depth", "0")
186+
assert(nestedRes == 1)
187+
assert(nestedOut.isEmpty)
188+
assert(nestedErr.contains("Parsing exceeded maximum recursion depth of 0"))
189+
}
190+
150191
test("maxStackDoesNotCountTailRecursiveCalls") {
151192
val (res, out, err) = runMain(
152193
"--exec",

0 commit comments

Comments
 (0)