From bf470b2d79a88b04c17f466e6f2bd1a0466ebe4d Mon Sep 17 00:00:00 2001 From: andreatp Date: Thu, 14 May 2026 12:45:51 +0100 Subject: [PATCH] Add support for %q format verb in sprintf (#175) Java's String.format doesn't support %q, so replace it with "%s" to match Go/Rego semantics of wrapping the argument in double quotes. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../java/com/styra/opa/wasm/builtins/String.java | 4 ++++ .../com/styra/opa/wasm/OpaStringBuiltinsTest.java | 15 ++++++++++++++- .../fixtures/string-builtins/policy.rego | 4 ++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/styra/opa/wasm/builtins/String.java b/core/src/main/java/com/styra/opa/wasm/builtins/String.java index 24c863f..023847a 100644 --- a/core/src/main/java/com/styra/opa/wasm/builtins/String.java +++ b/core/src/main/java/com/styra/opa/wasm/builtins/String.java @@ -64,6 +64,10 @@ private static JsonNode sprintfImpl(OpaWasm instance, JsonNode operand0, JsonNod if (format.contains("%v")) { format = format.replaceAll("%v", "%s"); } + // %q wraps the string in double quotes (Go/Rego semantics) + if (format.contains("%q")) { + format = format.replace("%q", "\"%s\""); + } var result = java.lang.String.format(format, args.toArray()); return TextNode.valueOf(result); diff --git a/core/src/test/java/com/styra/opa/wasm/OpaStringBuiltinsTest.java b/core/src/test/java/com/styra/opa/wasm/OpaStringBuiltinsTest.java index 8f523f8..1683a1d 100644 --- a/core/src/test/java/com/styra/opa/wasm/OpaStringBuiltinsTest.java +++ b/core/src/test/java/com/styra/opa/wasm/OpaStringBuiltinsTest.java @@ -16,7 +16,8 @@ public static void beforeAll() throws Exception { "string-builtins", "string_builtins/invoke_sprintf", "string_builtins/integer_fastpath", - "string_builtins/string_example") + "string_builtins/string_example", + "string_builtins/quoted") .resolve("policy.wasm"); } @@ -46,4 +47,16 @@ public void stringExample() { assertEquals("my string", result.get("printed").asText()); } + + @Test + public void quoted() { + var opa = OpaPolicy.builder().withPolicy(wasmFile).build(); + + var result = + Utils.getResult( + opa.entrypoint("string_builtins/quoted") + .evaluate("{\"value\": \"String\"}")); + + assertEquals("requested \"String\" is invalid", result.get("printed").asText()); + } } diff --git a/core/src/test/resources/fixtures/string-builtins/policy.rego b/core/src/test/resources/fixtures/string-builtins/policy.rego index c39e6a7..50858fa 100644 --- a/core/src/test/resources/fixtures/string-builtins/policy.rego +++ b/core/src/test/resources/fixtures/string-builtins/policy.rego @@ -11,3 +11,7 @@ integer_fastpath := { string_example := { "printed": sprintf("%s", ["my string"]) } + +quoted := { + "printed": sprintf("requested %q is invalid", [input.value]) +}