This is an automated email from the ASF dual-hosted git repository. luigidemasi pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-kit.git
commit 2cd0f0e33fd7a47f512f3ead3f53eaf618573a94 Author: Jinal Sarvaiya <[email protected]> AuthorDate: Thu Nov 5 08:53:29 2026 +0000 fix #100: reject mixed fenced/bare envelopes and null-padded array wrappers --- .../camel/kit/ship/controller/ShipStageResult.java | 11 +++++++++-- .../kit/ship/controller/ShipStageResultTest.java | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java index d91dbd00..7de329df 100644 --- a/kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java +++ b/kit/camel-kit-core/src/main/java/org/apache/camel/kit/ship/controller/ShipStageResult.java @@ -295,17 +295,24 @@ record ShipStageResult( Matcher m = JSON_FENCE.matcher(text); if (m.find()) { String first = m.group(1).strip(); + int fenceStart = m.start(); + int fenceEnd = m.end(); if (m.find()) { throw new IOException("Pi stage result contains conflicting fenced JSON envelopes"); } + String before = text.substring(0, fenceStart); + String after = text.substring(fenceEnd); + if (before.indexOf('{') >= 0 || after.indexOf('{') >= 0) { + throw new IOException("Pi stage result contains conflicting JSON envelopes"); + } return first; } int start = text.indexOf('{'); int end = text.lastIndexOf('}'); if (start >= 0 && end > start) { String prefix = text.substring(0, start).strip(); - if (!prefix.isEmpty() && prefix.charAt(prefix.length() - 1) == '[') { - return stripped; + if (!prefix.isEmpty() && prefix.indexOf('[') >= 0) { + throw new IOException("Pi stage result is wrapped in an array"); } return text.substring(start, end + 1); } diff --git a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipStageResultTest.java b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipStageResultTest.java index 0260c3a7..40100ada 100644 --- a/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipStageResultTest.java +++ b/kit/camel-kit-core/src/test/java/org/apache/camel/kit/ship/controller/ShipStageResultTest.java @@ -439,6 +439,21 @@ class ShipStageResultTest { assertRejected(Stage.DISCOVERY, multiFence, "conflicting fenced envelopes"); } + @Test + void rejectsMixedFencedAndBareEnvelopes() throws Exception { + ObjectNode fenced = result(null, null); + ObjectNode bare = result(null, null); + bare.put("materialAmbiguity", true); + bare.withArray("unansweredQuestions").addObject() + .put("question", "Which retry limit?").putNull("defaultApplied"); + String fencedThenBare = "```json\n" + JSON.writeValueAsString(fenced) + + "\n```\nCorrection:\n" + JSON.writeValueAsString(bare); + assertRejected(Stage.DESIGN, fencedThenBare, "fenced then bare envelope"); + String bareThenFenced = JSON.writeValueAsString(bare) + + "\nCorrected:\n```json\n" + JSON.writeValueAsString(fenced) + "\n```\n"; + assertRejected(Stage.DESIGN, bareThenFenced, "bare then fenced envelope"); + } + @Test void rejectsArrayWrappedEnvelope() throws Exception { ObjectNode valid = result("149-local-controller", null); @@ -446,6 +461,13 @@ class ShipStageResultTest { assertRejected(Stage.DISCOVERY, array, "array-wrapped envelope"); } + @Test + void rejectsNullPaddedArrayWrapper() throws Exception { + ObjectNode valid = result("149-local-controller", null); + String nullPadded = "[null, " + JSON.writeValueAsString(valid) + "]"; + assertRejected(Stage.DISCOVERY, nullPadded, "null-padded array wrapper"); + } + @Test void rejectsOversizedProseAroundValidFence() throws Exception { ObjectNode valid = result("149-local-controller", null);
