diff --git a/man/rules/meson.build b/man/rules/meson.build
index d7cbd5b652..d2d26abe5d 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -861,6 +861,17 @@ manpages = [
'sd_json_dispatch_variant',
'sd_json_dispatch_variant_noref'],
''],
+ ['sd_json_parse',
+ '3',
+ ['SD_JSON_PARSE_MUST_BE_ARRAY',
+ 'SD_JSON_PARSE_MUST_BE_OBJECT',
+ 'SD_JSON_PARSE_SENSITIVE',
+ 'sd_json_parse_continue',
+ 'sd_json_parse_file',
+ 'sd_json_parse_file_at',
+ 'sd_json_parse_with_source',
+ 'sd_json_parse_with_source_continue'],
+ ''],
['sd_listen_fds',
'3',
['SD_LISTEN_FDS_START', 'sd_listen_fds_with_names'],
diff --git a/man/sd_json_parse.xml b/man/sd_json_parse.xml
new file mode 100644
index 0000000000..c5234734da
--- /dev/null
+++ b/man/sd_json_parse.xml
@@ -0,0 +1,248 @@
+
+
+
+
+
+
+
+ sd_json_parse
+ systemd
+
+
+
+ sd_json_parse
+ 3
+
+
+
+ sd_json_parse
+ sd_json_parse_continue
+ sd_json_parse_with_source
+ sd_json_parse_with_source_continue
+ sd_json_parse_file
+ sd_json_parse_file_at
+ SD_JSON_PARSE_SENSITIVE
+ SD_JSON_PARSE_MUST_BE_OBJECT
+ SD_JSON_PARSE_MUST_BE_ARRAY
+
+ Parse JSON strings and files into JSON variant objects
+
+
+
+
+ #include <systemd/sd-json.h>
+
+
+ int sd_json_parse
+ const char *string
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+ int sd_json_parse_continue
+ const char **p
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+ int sd_json_parse_with_source
+ const char *string
+ const char *source
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+ int sd_json_parse_with_source_continue
+ const char **p
+ const char *source
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+ int sd_json_parse_file
+ FILE *f
+ const char *path
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+ int sd_json_parse_file_at
+ FILE *f
+ int dir_fd
+ const char *path
+ sd_json_parse_flags_t flags
+ sd_json_variant **ret
+ unsigned *reterr_line
+ unsigned *reterr_column
+
+
+
+
+
+ Description
+
+ sd_json_parse() parses the JSON string in string and
+ returns the resulting JSON variant object in ret. The input must contain exactly
+ one JSON value (object, array, string, number, boolean, or null); any trailing non-whitespace content
+ after the first parsed value is considered an error.
+
+ If parsing fails, the reterr_line and reterr_column
+ arguments are set to the line and column (both one-based) where the parse error occurred. One or both
+ may be passed as NULL if the caller is not interested in error location
+ information. On success, the return value is non-negative and ret is set to a
+ newly allocated JSON variant object (which must be freed with
+ sd_json_variant_unref3
+ when no longer needed). ret may be passed as NULL, in which
+ case the input is validated but no object is returned.
+
+ sd_json_parse_continue() is similar, but is intended for parsing a sequence of
+ concatenated JSON values from a single input string. Instead of taking a const char * string
+ directly, it takes a pointer to a const char * pointer. After each successful parse, the
+ pointer is advanced past the consumed input, so that subsequent calls will parse the next JSON
+ value. This is useful for parsing newline-delimited JSON (NDJSON) streams or similar concatenated JSON
+ formats. Unlike sd_json_parse(), trailing content after the first JSON value is not
+ considered an error — it is expected to be the beginning of the next value.
+
+ sd_json_parse_with_source() and
+ sd_json_parse_with_source_continue() are similar to
+ sd_json_parse() and sd_json_parse_continue(), respectively, but
+ take an additional source argument. This is a human-readable string (typically a
+ file name or other origin identifier) that is attached to the parsed JSON variant object and can later be
+ retrieved via
+ sd_json_variant_get_source3. If
+ source is NULL, no source information is
+ attached. sd_json_parse() and sd_json_parse_continue() are
+ equivalent to calling their _with_source counterparts with
+ source set to NULL.
+
+ sd_json_parse_file() reads and parses a JSON value from a file. If the
+ f argument is non-NULL, the JSON text is read from the
+ specified FILE stream. If f is NULL, the file
+ indicated by path is opened and read instead. The path
+ argument serves a dual purpose: it is both used for opening the file (if f is
+ NULL) and recorded as source information in the resulting JSON variant (see
+ above).
+
+ sd_json_parse_file_at() is similar to
+ sd_json_parse_file(), but takes an additional dir_fd argument
+ which specifies a file descriptor referring to the directory to resolve relative paths specified in
+ path against. If set to AT_FDCWD, relative paths are resolved
+ against the current working directory, which is the default behaviour of
+ sd_json_parse_file().
+
+ The flags argument is a bitmask of zero or more of the following
+ flags:
+
+
+
+ SD_JSON_PARSE_SENSITIVE
+
+ Marks the resulting JSON variant as "sensitive", indicating that it contains secret
+ key material or similar confidential data. Sensitive variants are erased from memory when freed and
+ are excluded from certain debug logging and introspection operations. See
+ sd_json_variant_sensitive3
+ for details.
+
+
+
+ SD_JSON_PARSE_MUST_BE_OBJECT
+
+ Requires that the top-level JSON value be a JSON object (i.e. {…}).
+ If the top-level value is an array, string, number, boolean, or null, parsing fails with
+ -EINVAL.
+
+
+
+
+
+ SD_JSON_PARSE_MUST_BE_ARRAY
+
+ Requires that the top-level JSON value be a JSON array (i.e. […]).
+ If the top-level value is an object, string, number, boolean, or null, parsing fails with
+ -EINVAL.
+
+
+
+
+
+ If both SD_JSON_PARSE_MUST_BE_OBJECT and
+ SD_JSON_PARSE_MUST_BE_ARRAY are set, both objects and arrays are accepted, but
+ non-container values (strings, numbers, booleans, null) are still refused.
+
+
+
+ Return Value
+
+ On success, these functions return 0. On failure, they return a negative errno-style error
+ code.
+
+
+ Errors
+
+ Returned errors may indicate the following problems:
+
+
+
+ -EINVAL
+
+ The input is not valid JSON, the input contains trailing content after the parsed
+ value (only for non-_continue variants), or a top-level type constraint
+ specified via SD_JSON_PARSE_MUST_BE_OBJECT or
+ SD_JSON_PARSE_MUST_BE_ARRAY was violated.
+
+
+
+ -ENODATA
+
+ The input string is empty or NULL.
+
+
+
+ -ENOMEM
+
+ Memory allocation failed.
+
+
+
+
+
+
+
+
+ History
+ sd_json_parse(),
+ sd_json_parse_continue(),
+ sd_json_parse_with_source(),
+ sd_json_parse_with_source_continue(),
+ sd_json_parse_file(), and
+ sd_json_parse_file_at() were added in version 257.
+
+
+
+ See Also
+
+
+ systemd1
+ sd-json3
+ sd_json_variant_unref3
+ sd_json_variant_get_source3
+ sd_json_dispatch3
+
+
+
diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c
index 0f763f75e9..bdbd44910b 100644
--- a/src/analyze/analyze-security.c
+++ b/src/analyze/analyze-security.c
@@ -3,6 +3,7 @@
#include
#include "sd-bus.h"
+#include "sd-json.h"
#include "alloc-util.h"
#include "analyze-verify-util.h"
@@ -2919,7 +2920,7 @@ int verb_security(int argc, char *argv[], uintptr_t _data, void *userdata) {
unsigned line = 0, column = 0;
if (arg_security_policy) {
- r = sd_json_parse_file(/* f= */ NULL, arg_security_policy, /* flags= */ 0, &policy, &line, &column);
+ r = sd_json_parse_file(/* f= */ NULL, arg_security_policy, SD_JSON_PARSE_MUST_BE_OBJECT, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
} else {
@@ -2931,7 +2932,7 @@ int verb_security(int argc, char *argv[], uintptr_t _data, void *userdata) {
return r;
if (f) {
- r = sd_json_parse_file(f, pp, /* flags= */ 0, &policy, &line, &column);
+ r = sd_json_parse_file(f, pp, SD_JSON_PARSE_MUST_BE_OBJECT, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
}
diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c
index 0f655a4694..f41d6fef31 100644
--- a/src/coredump/coredumpctl.c
+++ b/src/coredump/coredumpctl.c
@@ -829,7 +829,7 @@ static int print_info(FILE *file, sd_journal *j, bool need_space) {
if (exe && pkgmeta_json) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = sd_json_parse(pkgmeta_json, 0, &v, NULL, NULL);
+ r = sd_json_parse(pkgmeta_json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0) {
_cleanup_free_ char *esc = cescape(pkgmeta_json);
log_warning_errno(r, "json_parse on \"%s\" failed, ignoring: %m", strnull(esc));
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
index 02ed4dd273..a2804e033a 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
@@ -162,7 +162,7 @@ _public_ int cryptsetup_token_validate(
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
index 4c6e28500a..16cf910fe6 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
@@ -115,7 +115,7 @@ _public_ int cryptsetup_token_validate(
sd_json_variant *w;
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m.");
diff --git a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
index 933d18e2fd..58dc37c5bf 100644
--- a/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
+++ b/src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
@@ -72,7 +72,7 @@ _public_ int cryptsetup_token_open_pin(
if (usrptr)
params = *(systemd_tpm2_plugin_params *)usrptr;
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Failed to parse token JSON data: %m");
@@ -186,7 +186,7 @@ _public_ void cryptsetup_token_dump(
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " JSON object: %m");
@@ -275,7 +275,7 @@ _public_ int cryptsetup_token_validate(
assert(json);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m");
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
index 18b0e4f37f..c6cfdcf6ef 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
@@ -97,7 +97,7 @@ int parse_luks2_fido2_data(
assert(ret_cid_size);
assert(ret_required);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return crypt_log_error_errno(cd, r, "Failed to parse JSON token data: %m");
diff --git a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
index 9f11f81c4a..723265479c 100644
--- a/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
+++ b/src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
@@ -246,7 +246,7 @@ int parse_luks2_pkcs11_data(
assert(ret_encrypted_key);
assert(ret_encrypted_key_size);
- r = sd_json_parse(json, 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return r;
diff --git a/src/home/homectl.c b/src/home/homectl.c
index 507860cde6..db4e6639d5 100644
--- a/src/home/homectl.c
+++ b/src/home/homectl.c
@@ -758,7 +758,7 @@ static int inspect_home(sd_bus *bus, const char *name) {
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
@@ -1159,7 +1159,11 @@ static int acquire_new_home_record(sd_json_variant *input, UserRecord **ret) {
r = sd_json_parse_file(
streq(arg_identity, "-") ? stdin : NULL,
- streq(arg_identity, "-") ? "" : arg_identity, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ streq(arg_identity, "-") ? "" : arg_identity,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &v,
+ &line,
+ &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
} else
@@ -1667,7 +1671,7 @@ static int register_home_one(sd_bus *bus, FILE *f, const char *path) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse_file(f, path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(f, path, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse user record: %m", path, line, column);
@@ -1785,7 +1789,11 @@ static int acquire_updated_home_record(
r = sd_json_parse_file(
streq(arg_identity, "-") ? stdin : NULL,
- streq(arg_identity, "-") ? "" : arg_identity, SD_JSON_PARSE_SENSITIVE, &json, &line, &column);
+ streq(arg_identity, "-") ? "" : arg_identity,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &json,
+ &line,
+ &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
@@ -1822,7 +1830,12 @@ static int acquire_updated_home_record(
if (incomplete)
return log_error_errno(SYNTHETIC_ERRNO(EACCES), "Lacking rights to acquire user record including privileged metadata, can't update record.");
- r = sd_json_parse(text, SD_JSON_PARSE_SENSITIVE, &json, NULL, NULL);
+ r = sd_json_parse(
+ text,
+ SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE,
+ &json,
+ /* reterr_line= */ NULL,
+ /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
@@ -2569,7 +2582,7 @@ static int create_or_register_from_credentials(void) {
/* f= */ NULL,
fd,
de->d_name,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&identity,
&line,
&column);
@@ -5135,7 +5148,7 @@ static int fallback_shell(int argc, char *argv[]) {
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON identity: %m");
diff --git a/src/home/homed-bus.c b/src/home/homed-bus.c
index c96ecf6620..f185e87295 100644
--- a/src/home/homed-bus.c
+++ b/src/home/homed-bus.c
@@ -24,7 +24,7 @@ int bus_message_read_secret(sd_bus_message *m, UserRecord **ret, sd_bus_error *e
if (r < 0)
return r;
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON secret record at %u:%u: %m", line, column);
@@ -57,7 +57,7 @@ int bus_message_read_home_record(sd_bus_message *m, UserRecordLoadFlags flags, U
if (r < 0)
return r;
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE|SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Failed to parse JSON identity record at %u:%u: %m", line, column);
diff --git a/src/home/homed-home.c b/src/home/homed-home.c
index 00b2e72f9f..12e0eed2da 100644
--- a/src/home/homed-home.c
+++ b/src/home/homed-home.c
@@ -589,7 +589,7 @@ static int home_parse_worker_stdout(int _fd, UserRecord **ret) {
}
unsigned line = 0, column = 0;
- r = sd_json_parse_file(f, "stdout", SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(f, "stdout", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse identity at %u:%u: %m", line, column);
diff --git a/src/home/homed-manager.c b/src/home/homed-manager.c
index 85c92192f4..c9d43982d0 100644
--- a/src/home/homed-manager.c
+++ b/src/home/homed-manager.c
@@ -406,9 +406,9 @@ static int manager_add_home_by_record(
goto unlink_this_file;
unsigned line = 0, column = 0;
- r = sd_json_parse_file_at(NULL, dir_fd, fname, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file_at(/* f= */ NULL, dir_fd, fname, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
- return log_error_errno(r, "Failed to parse identity record at %s:%u%u: %m", fname, line, column);
+ return log_error_errno(r, "Failed to parse identity record at %s:%u:%u: %m", fname, line, column);
if (sd_json_variant_is_blank_object(v))
goto unlink_this_file;
diff --git a/src/home/homework-luks.c b/src/home/homework-luks.c
index 9f7af06a4e..caa05db26f 100644
--- a/src/home/homework-luks.c
+++ b/src/home/homework-luks.c
@@ -887,7 +887,7 @@ static int luks_validate_home_record(
return log_error_errno(r, "Failed to read LUKS token %i: %m", token);
unsigned line = 0, column = 0;
- r = sd_json_parse(text, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse LUKS token JSON data %u:%u: %m", line, column);
@@ -940,7 +940,7 @@ static int luks_validate_home_record(
decrypted[decrypted_size] = 0;
- r = sd_json_parse(decrypted, SD_JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
+ r = sd_json_parse(decrypted, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &rr, NULL, NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse decrypted JSON record, refusing.");
diff --git a/src/home/homework.c b/src/home/homework.c
index e796f125fb..2efd3ddb60 100644
--- a/src/home/homework.c
+++ b/src/home/homework.c
@@ -571,7 +571,7 @@ static int read_identity_file(int root_fd, sd_json_variant **ret) {
return log_oom();
unsigned line = 0, column = 0;
- r = sd_json_parse_file(identity_file, ".identity", SD_JSON_PARSE_SENSITIVE, ret, &line, &column);
+ r = sd_json_parse_file(identity_file, ".identity", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, ret, &line, &column);
if (r < 0)
return log_error_errno(r, "[.identity:%u:%u] Failed to parse JSON data: %m", line, column);
@@ -2025,7 +2025,7 @@ static int run(int argc, char *argv[]) {
}
unsigned line = 0, column = 0;
- r = sd_json_parse_file(json_file, json_path, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file(json_file, json_path, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON data: %m", json_path, line, column);
diff --git a/src/home/pam_systemd_home.c b/src/home/pam_systemd_home.c
index c58a343376..e8d7282cbf 100644
--- a/src/home/pam_systemd_home.c
+++ b/src/home/pam_systemd_home.c
@@ -194,7 +194,7 @@ static int acquire_user_record(
fresh_data = true;
}
- r = sd_json_parse(json, /* flags= */ 0, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse JSON user record: %m");
diff --git a/src/hostname/hostnamectl.c b/src/hostname/hostnamectl.c
index d0ceceb155..75a17a13ae 100644
--- a/src/hostname/hostnamectl.c
+++ b/src/hostname/hostnamectl.c
@@ -567,7 +567,7 @@ static int verb_show_status(int argc, char *argv[], uintptr_t _data, void *userd
if (r < 0)
return bus_log_parse_error(r);
- r = sd_json_parse(text, 0, &v, NULL, NULL);
+ r = sd_json_parse(text, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON: %m");
diff --git a/src/import/pull-oci.c b/src/import/pull-oci.c
index cbbad44eb1..0f16c65630 100644
--- a/src/import/pull-oci.c
+++ b/src/import/pull-oci.c
@@ -200,7 +200,7 @@ int oci_pull_new(
return 0;
}
-static int pull_job_payload_as_json(PullJob *j, sd_json_variant **ret) {
+static int pull_job_payload_as_json_object(PullJob *j, sd_json_variant **ret) {
int r;
assert(j);
@@ -214,7 +214,7 @@ static int pull_job_payload_as_json(PullJob *j, sd_json_variant **ret) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse((char*) j->payload.iov_base, /* flags= */ 0, &v, &line, &column);
+ r = sd_json_parse((char*) j->payload.iov_base, SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON at position %u:%u: %m", line, column);
@@ -391,7 +391,7 @@ static int oci_pull_process_index(OciPull *i, PullJob *j) {
* https://github.com/opencontainers/image-spec/blob/main/image-index.md */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
return r;
@@ -783,7 +783,7 @@ static int oci_pull_process_manifest(OciPull *i, PullJob *j) {
* https://github.com/opencontainers/image-spec/blob/main/manifest.md */
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
return r;
@@ -958,7 +958,7 @@ static int oci_pull_save_nspawn_settings(OciPull *i) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse((char*) i->config.iov_base, /* flags= */ 0, &v, &line, &column);
+ r = sd_json_parse((char*) i->config.iov_base, SD_JSON_PARSE_MUST_BE_OBJECT, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse JSON config data at position %u:%u: %m", line, column);
@@ -1340,7 +1340,7 @@ static void oci_pull_job_on_finished_bearer_token(PullJob *j) {
goto finish;
}
- r = pull_job_payload_as_json(j, &v);
+ r = pull_job_payload_as_json_object(j, &v);
if (r < 0)
goto finish;
diff --git a/src/libsystemd-network/sd-dhcp-server-lease.c b/src/libsystemd-network/sd-dhcp-server-lease.c
index 5c24de4084..0268bbf2c2 100644
--- a/src/libsystemd-network/sd-dhcp-server-lease.c
+++ b/src/libsystemd-network/sd-dhcp-server-lease.c
@@ -490,7 +490,7 @@ static int load_leases_file(int dir_fd, const char *path, SavedInfo *ret) {
/* f= */ NULL,
dir_fd,
path,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&v,
/* reterr_line= */ NULL,
/* ret_column= */ NULL);
diff --git a/src/libsystemd/sd-json/sd-json.c b/src/libsystemd/sd-json/sd-json.c
index 7829e11880..b959fe1628 100644
--- a/src/libsystemd/sd-json/sd-json.c
+++ b/src/libsystemd/sd-json/sd-json.c
@@ -3039,7 +3039,6 @@ static int json_parse_internal(
int r;
assert_return(input, -EINVAL);
- assert_return(ret, -EINVAL);
p = *input;
@@ -3111,12 +3110,16 @@ static int json_parse_internal(
break;
case JSON_TOKEN_OBJECT_OPEN:
-
if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_FIRST_ELEMENT, EXPECT_ARRAY_NEXT_ELEMENT)) {
r = -EINVAL;
goto finish;
}
+ if (n_stack == 1 && FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_ARRAY) && !FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_OBJECT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (!GREEDY_REALLOC(stack, n_stack+1)) {
r = -ENOMEM;
goto finish;
@@ -3168,6 +3171,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && !FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_ARRAY) && FLAGS_SET(flags, SD_JSON_PARSE_MUST_BE_OBJECT)) {
+ r = -EINVAL;
+ goto finish;
+ }
+
if (!GREEDY_REALLOC(stack, n_stack+1)) {
r = -ENOMEM;
goto finish;
@@ -3217,6 +3225,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_string(&add, string);
if (r < 0)
goto finish;
@@ -3240,6 +3253,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_real(&add, value.real);
if (r < 0)
goto finish;
@@ -3261,6 +3279,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_integer(&add, value.integer);
if (r < 0)
goto finish;
@@ -3282,6 +3305,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_unsigned(&add, value.unsig);
if (r < 0)
goto finish;
@@ -3303,6 +3331,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_boolean(&add, value.boolean);
if (r < 0)
goto finish;
@@ -3324,6 +3357,11 @@ static int json_parse_internal(
goto finish;
}
+ if (n_stack == 1 && (flags & (SD_JSON_PARSE_MUST_BE_ARRAY|SD_JSON_PARSE_MUST_BE_OBJECT)) != 0) {
+ r = -EINVAL;
+ goto finish;
+ }
+
r = sd_json_variant_new_null(&add);
if (r < 0)
goto finish;
@@ -3365,7 +3403,8 @@ done:
assert(n_stack == 1);
assert(stack[0].n_elements == 1);
- *ret = sd_json_variant_ref(stack[0].elements[0]);
+ if (ret)
+ *ret = sd_json_variant_ref(stack[0].elements[0]);
*input = p;
r = 0;
diff --git a/src/libsystemd/sd-varlink/sd-varlink.c b/src/libsystemd/sd-varlink/sd-varlink.c
index 7ee85e9789..c1ffaedfc8 100644
--- a/src/libsystemd/sd-varlink/sd-varlink.c
+++ b/src/libsystemd/sd-varlink/sd-varlink.c
@@ -1009,14 +1009,14 @@ static int varlink_parse_message(sd_varlink *v) {
sz = e - begin + 1;
- r = sd_json_parse(begin, 0, &v->current, NULL, NULL);
+ r = sd_json_parse(begin, SD_JSON_PARSE_MUST_BE_OBJECT, &v->current, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (v->input_sensitive)
explicit_bzero_safe(begin, sz);
if (r < 0) {
/* If we encounter a parse failure flush all data. We cannot possibly recover from this,
* hence drop all buffered data now. */
v->input_buffer_index = v->input_buffer_size = v->input_buffer_unscanned = 0;
- return varlink_log_errno(v, r, "Failed to parse JSON: %m");
+ return varlink_log_errno(v, r, "Failed to parse JSON object: %m");
}
if (v->input_sensitive) {
diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c
index f7aa6f9b8f..ec862e7d4f 100644
--- a/src/login/pam_systemd.c
+++ b/src/login/pam_systemd.c
@@ -205,7 +205,7 @@ static int acquire_user_record(pam_handle_t *pamh, UserRecord **ret_record) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
/* Parse cached record */
- r = sd_json_parse(json, SD_JSON_PARSE_SENSITIVE, &v, NULL, NULL);
+ r = sd_json_parse(json, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
return pam_syslog_errno(pamh, LOG_ERR, r, "Failed to parse JSON user record: %m");
diff --git a/src/measure/measure-tool.c b/src/measure/measure-tool.c
index 2f37d58381..d632bb62f5 100644
--- a/src/measure/measure-tool.c
+++ b/src/measure/measure-tool.c
@@ -820,13 +820,9 @@ static int build_policy_digest(bool sign) {
assert(!strv_isempty(arg_phase));
if (arg_append) {
- r = sd_json_parse_file(NULL, arg_append, 0, &v, NULL, NULL);
+ r = sd_json_parse_file(/* f= */ NULL, arg_append, SD_JSON_PARSE_MUST_BE_OBJECT, &v, /* reterr_line= */ NULL, /* reterr_column= */ NULL);
if (r < 0)
- return log_error_errno(r, "Failed to parse '%s': %m", arg_append);
-
- if (!sd_json_variant_is_object(v))
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
- "File '%s' is not a valid JSON object, refusing.", arg_append);
+ return log_error_errno(r, "Failed to parse JSON object '%s': %m", arg_append);
}
/* When signing/building digest we only support JSON output */
diff --git a/src/systemd/sd-json.h b/src/systemd/sd-json.h
index 359149ef9e..6a19775490 100644
--- a/src/systemd/sd-json.h
+++ b/src/systemd/sd-json.h
@@ -181,7 +181,9 @@ int sd_json_variant_sort(sd_json_variant **v);
int sd_json_variant_normalize(sd_json_variant **v);
__extension__ typedef enum _SD_ENUM_TYPE_S64(sd_json_parse_flags_t) {
- SD_JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
+ SD_JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */
+ SD_JSON_PARSE_MUST_BE_OBJECT = 1 << 1, /* refuse parsing if top-level is not an object */
+ SD_JSON_PARSE_MUST_BE_ARRAY = 1 << 2, /* refuse parsing if top-level is not an array */
_SD_ENUM_FORCE_S64(JSON_PARSE_FLAGS)
} sd_json_parse_flags_t;
diff --git a/src/test/test-json.c b/src/test/test-json.c
index 679152fd95..4994d42abc 100644
--- a/src/test/test-json.c
+++ b/src/test/test-json.c
@@ -1588,4 +1588,46 @@ TEST(json_variant_compare) {
test_json_variant_compare_one("{\"a\":\"b\",\"b\":\"c\"}", "{\"a\":\"b\"}", 1);
}
+TEST(must_be) {
+ ASSERT_OK(sd_json_parse("null", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("null", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("true", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("true", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("\"foo\"", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("\"foo\"", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("4711", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("4711", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("-4711", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4711", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("-4.5", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_ERROR(sd_json_parse("-4.5", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+
+ ASSERT_OK(sd_json_parse("{}", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_OK(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_OK(sd_json_parse("{}", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+
+ ASSERT_OK(sd_json_parse("[]", /* flags= */ 0, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_ERROR(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_OBJECT, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL), EINVAL);
+ ASSERT_OK(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+ ASSERT_OK(sd_json_parse("[]", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_MUST_BE_ARRAY, /* ret= */ NULL, /* reterr_line= */ NULL, /* reterr_column= */ NULL));
+}
+
DEFINE_TEST_MAIN(LOG_DEBUG);
diff --git a/src/userdb/userdbctl.c b/src/userdb/userdbctl.c
index b0d7a06941..8da35172c5 100644
--- a/src/userdb/userdbctl.c
+++ b/src/userdb/userdbctl.c
@@ -1260,7 +1260,7 @@ static int load_credential_one(
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
unsigned line = 0, column = 0;
- r = sd_json_parse_file_at(NULL, credential_dir_fd, name, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+ r = sd_json_parse_file_at(/* f= */ NULL, credential_dir_fd, name, SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse credential '%s' as JSON at %u:%u: %m", name, line, column);
@@ -1844,7 +1844,7 @@ static int parse_argv(int argc, char *argv[]) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *v = NULL;
const char *fn = streq(optarg, "-") ? NULL : optarg;
unsigned line = 0;
- r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "", SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
+ r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "", SD_JSON_PARSE_MUST_BE_OBJECT|SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
if (r < 0)
return log_syntax(/* unit= */ NULL, LOG_ERR, fn ?: "", line, r, "JSON parse failure.");
diff --git a/src/varlinkctl/varlinkctl.c b/src/varlinkctl/varlinkctl.c
index 5048f433b1..775a481ae8 100644
--- a/src/varlinkctl/varlinkctl.c
+++ b/src/varlinkctl/varlinkctl.c
@@ -682,14 +682,14 @@ static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) {
source = "";
/* is correct, as dispatch_verb() shifts arguments by one for the verb. */
- r = sd_json_parse_with_source(parameter, source, 0, &jp, &line, &column);
+ r = sd_json_parse_with_source(parameter, source, SD_JSON_PARSE_MUST_BE_OBJECT, &jp, &line, &column);
} else {
if (isatty_safe(STDIN_FILENO) && !arg_quiet)
log_notice("Expecting method call parameter JSON object on standard input. (Provide empty string or {} for no parameters.)");
source = "";
- r = sd_json_parse_file_at(stdin, AT_FDCWD, source, 0, &jp, &line, &column);
+ r = sd_json_parse_file_at(stdin, AT_FDCWD, source, SD_JSON_PARSE_MUST_BE_OBJECT, &jp, &line, &column);
}
if (r < 0 && r != -ENODATA)
return log_error_errno(r, "Failed to parse parameters at %s:%u:%u: %m", source, line, column);
diff --git a/src/vmspawn/vmspawn-util.c b/src/vmspawn/vmspawn-util.c
index 3f65fab2cc..c6e258c50a 100644
--- a/src/vmspawn/vmspawn-util.c
+++ b/src/vmspawn/vmspawn-util.c
@@ -253,7 +253,7 @@ static int load_firmware_data(const char *path, FirmwareData **ret) {
r = sd_json_parse_file(
/* f= */ NULL,
path,
- /* flags= */ 0,
+ /* flags= */ SD_JSON_PARSE_MUST_BE_OBJECT,
&json,
/* reterr_line= */ NULL,
/* ret_column= */ NULL);