diff --git a/docs/content/policy-reference.md b/docs/content/policy-reference.md
index 14f862cd29..426d343f26 100644
--- a/docs/content/policy-reference.md
+++ b/docs/content/policy-reference.md
@@ -488,7 +488,7 @@ The following table shows examples of how ``glob.match`` works:
| ``output := urlquery.decode_object(string)`` | ``output`` is URL query parameter decoded ``string`` represented as an ``object`` | ``SDK-dependent`` |
| ``output := json.marshal(x)`` | ``output`` is ``x`` serialized to a JSON string | ✅ |
| ``output := json.unmarshal(string)`` | ``output`` is ``string`` deserialized to a term from a JSON encoded string | ✅ |
-| ``output := json.is_valid(string)`` | ``output`` is a ``boolean`` that indicated whether ``string`` is a valid JSON document | ``SDK-dependent`` |
+| ``output := json.is_valid(string)`` | ``output`` is a ``boolean`` that indicated whether ``string`` is a valid JSON document | ✅ |
| ``output := yaml.marshal(x)`` | ``output`` is ``x`` serialized to a YAML string | ``SDK-dependent`` |
| ``output := yaml.unmarshal(string)`` | ``output`` is ``string`` deserialized to a term from YAML encoded string | ``SDK-dependent`` |
| ``output := yaml.is_valid(string)`` | ``output`` is a ``boolean`` that indicated whether ``string`` is a valid YAML document that can be decoded by `yaml.unmarshal` | ``SDK-dependent`` |
diff --git a/internal/compiler/wasm/wasm.go b/internal/compiler/wasm/wasm.go
index 77b0150af4..63cfac8bc5 100644
--- a/internal/compiler/wasm/wasm.go
+++ b/internal/compiler/wasm/wasm.go
@@ -137,6 +137,7 @@ var builtinsFunctions = map[string]string{
ast.GlobMatch.Name: "opa_glob_match",
ast.JSONMarshal.Name: "opa_json_marshal",
ast.JSONUnmarshal.Name: "opa_json_unmarshal",
+ ast.JSONIsValid.Name: "opa_json_is_valid",
ast.ObjectFilter.Name: "builtin_object_filter",
ast.ObjectGet.Name: "builtin_object_get",
ast.ObjectRemove.Name: "builtin_object_remove",
diff --git a/wasm/src/encoding.c b/wasm/src/encoding.c
index b136f381a5..82e2d8b727 100644
--- a/wasm/src/encoding.c
+++ b/wasm/src/encoding.c
@@ -39,6 +39,7 @@
#include
#include "json.h"
+#include "malloc.h"
#include "value.h"
static const unsigned char base64_table[65] =
@@ -316,3 +317,24 @@ opa_value *opa_json_marshal(opa_value *a)
return opa_string_allocated(v, strlen(v));
}
+
+OPA_BUILTIN
+opa_value *opa_json_is_valid(opa_value *a)
+{
+ if (opa_value_type(a) != OPA_STRING)
+ {
+ return NULL;
+ }
+
+
+ opa_string_t *s = opa_cast_string(a);
+ opa_value *r = opa_json_parse(s->v, s->len);
+
+ if (r == NULL)
+ {
+ return opa_boolean(false);
+ }
+
+ opa_free(r);
+ return opa_boolean(true);
+}
\ No newline at end of file
diff --git a/wasm/src/encoding.h b/wasm/src/encoding.h
index c492f2c3e1..95d1fc427b 100644
--- a/wasm/src/encoding.h
+++ b/wasm/src/encoding.h
@@ -10,5 +10,6 @@ opa_value *opa_base64_url_decode(opa_value *a);
opa_value *opa_base64_url_encode(opa_value *a);
opa_value *opa_json_unmarshal(opa_value *a);
opa_value *opa_json_marshal(opa_value *a);
+opa_value *opa_json_is_valid(opa_value *a);
#endif
diff --git a/wasm/tests/test.c b/wasm/tests/test.c
index c3932d2fe2..95ded5b4b8 100644
--- a/wasm/tests/test.c
+++ b/wasm/tests/test.c
@@ -1790,6 +1790,8 @@ void test_json(void)
{
test("json/marshal", opa_value_compare(opa_json_marshal(opa_string_terminated("string")), opa_string_terminated("\"string\"")) == 0);
test("json/unmarshal", opa_value_compare(opa_json_unmarshal(opa_string_terminated("\"string\"")), opa_string_terminated("string")) == 0);
+ test("json/is_valid_true", opa_cast_boolean(opa_json_is_valid(opa_string_terminated("\"string\"")))->v);
+ test("json/is_valid_false", !opa_cast_boolean(opa_json_is_valid(opa_string_terminated("\"string")))->v);
}
WASM_EXPORT(test_object)