json: add JSON_FORMAT_REFUSE_SENSITIVE to json_variant_format()

Returns -EPERM if any node in the variant is marked as sensitive,
useful to avoid leaking data to log messages and so on
This commit is contained in:
Luca Boccassi
2024-01-03 11:54:48 +01:00
parent caef0bc3dc
commit fa9a6db478
3 changed files with 143 additions and 11 deletions
+26
View File
@@ -1771,6 +1771,28 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
return 0;
}
static bool json_variant_is_sensitive_recursive(JsonVariant *v) {
if (!v)
return false;
if (json_variant_is_sensitive(v))
return true;
if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
return false;
if (!json_variant_is_regular(v))
return false;
if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
return false;
if (v->is_reference)
return json_variant_is_sensitive_recursive(v->reference);
for (size_t i = 0; i < json_variant_elements(v); i++)
if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i)))
return true;
return false;
}
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
_cleanup_(memstream_done) MemStream m = {};
size_t sz;
@@ -1786,6 +1808,10 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
if (flags & JSON_FORMAT_OFF)
return -ENOEXEC;
if ((flags & JSON_FORMAT_REFUSE_SENSITIVE))
if (json_variant_is_sensitive_recursive(v))
return -EPERM;
f = memstream_init(&m);
if (!f)
return -ENOMEM;
+12 -11
View File
@@ -185,17 +185,18 @@ struct json_variant_foreach_state {
int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
typedef enum JsonFormatFlags {
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */
JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */
JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */
JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */
JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
JSON_FORMAT_REFUSE_SENSITIVE = 1 << 11, /* return EPERM if any node in the tree is marked as senstitive */
} JsonFormatFlags;
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
+105
View File
@@ -104,6 +104,17 @@ static void test_variant_one(const char *data, Test test) {
assert_se(json_variant_has_type(w, json_variant_type(v)));
assert_se(json_variant_equal(v, w));
s = mfree(s);
r = json_variant_format(w, JSON_FORMAT_REFUSE_SENSITIVE, &s);
assert_se(r == -EPERM);
assert_se(!s);
s = mfree(s);
r = json_variant_format(w, JSON_FORMAT_PRETTY, &s);
assert_se(r >= 0);
assert_se(s);
assert_se((size_t) r == strlen(s));
s = mfree(s);
w = json_variant_unref(w);
@@ -813,4 +824,98 @@ TEST(json_dispatch) {
assert_se(foobar.l == INT16_MIN);
}
TEST(json_sensitive) {
_cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL, *v = NULL;
_cleanup_free_ char *s = NULL;
int r;
assert_se(json_build(&a, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "bar", "baz", "foo", "qux", "baz"))) >= 0);
assert_se(json_build(&b, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "qux"))) >= 0);
json_variant_sensitive(a);
assert_se(json_variant_format(a, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
assert_se(!s);
r = json_variant_format(b, JSON_FORMAT_REFUSE_SENSITIVE, &s);
assert_se(r >= 0);
assert_se(s);
assert_se((size_t) r == strlen(s));
s = mfree(s);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
assert_se(r >= 0);
assert_se(s);
assert_se((size_t) r == strlen(s));
s = mfree(s);
v = json_variant_unref(v);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_VARIANT("b", b),
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s);
assert_se(r >= 0);
assert_se(s);
assert_se((size_t) r == strlen(s));
s = mfree(s);
v = json_variant_unref(v);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_VARIANT("b", b),
JSON_BUILD_PAIR_VARIANT("a", a),
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
assert_se(!s);
v = json_variant_unref(v);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_VARIANT("b", b),
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR_VARIANT("a", a),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
assert_se(!s);
v = json_variant_unref(v);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_VARIANT("b", b),
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR_VARIANT("a", a),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
assert_se(!s);
v = json_variant_unref(v);
assert_se(json_build(&v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR_VARIANT("b", b),
JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)),
JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")),
JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT),
JSON_BUILD_PAIR_VARIANT("a", a))) >= 0);
json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL);
assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM);
assert_se(!s);
}
DEFINE_TEST_MAIN(LOG_DEBUG);