]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-json: when parsing optionally insist top-level variant is object or array
authorLennart Poettering <lennart@amutable.com>
Thu, 19 Mar 2026 10:23:45 +0000 (11:23 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Fri, 20 Mar 2026 14:34:56 +0000 (15:34 +0100)
Typically, the top-level JSON object has to be an object, in any json
document we parse, hence let's add a simple way to enforce that.

Make use of this in various places.

(Note, various other JSON parsers insist on this logic right from the
beginning, but I actually thinking making this insisting optional like
this patch does it is the cleaner approach)

28 files changed:
man/rules/meson.build
man/sd_json_parse.xml [new file with mode: 0644]
src/analyze/analyze-security.c
src/coredump/coredumpctl.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-pkcs11.c
src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-tpm2.c
src/cryptsetup/cryptsetup-tokens/luks2-fido2.c
src/cryptsetup/cryptsetup-tokens/luks2-pkcs11.c
src/home/homectl.c
src/home/homed-bus.c
src/home/homed-home.c
src/home/homed-manager.c
src/home/homework-luks.c
src/home/homework.c
src/home/pam_systemd_home.c
src/hostname/hostnamectl.c
src/import/pull-oci.c
src/libsystemd-network/sd-dhcp-server-lease.c
src/libsystemd/sd-json/sd-json.c
src/libsystemd/sd-varlink/sd-varlink.c
src/login/pam_systemd.c
src/measure/measure-tool.c
src/systemd/sd-json.h
src/test/test-json.c
src/userdb/userdbctl.c
src/varlinkctl/varlinkctl.c
src/vmspawn/vmspawn-util.c

index d7cbd5b65201bf581da8cc8f4e4a92a8b15a508e..d2d26abe5da31859fae99845fc21490306e2cf99 100644 (file)
@@ -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 (file)
index 0000000..c523473
--- /dev/null
@@ -0,0 +1,248 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.5//EN"
+  "http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
+<!-- SPDX-License-Identifier: LGPL-2.1-or-later -->
+
+<refentry id="sd_json_parse" xmlns:xi="http://www.w3.org/2001/XInclude">
+
+  <refentryinfo>
+    <title>sd_json_parse</title>
+    <productname>systemd</productname>
+  </refentryinfo>
+
+  <refmeta>
+    <refentrytitle>sd_json_parse</refentrytitle>
+    <manvolnum>3</manvolnum>
+  </refmeta>
+
+  <refnamediv>
+    <refname>sd_json_parse</refname>
+    <refname>sd_json_parse_continue</refname>
+    <refname>sd_json_parse_with_source</refname>
+    <refname>sd_json_parse_with_source_continue</refname>
+    <refname>sd_json_parse_file</refname>
+    <refname>sd_json_parse_file_at</refname>
+    <refname>SD_JSON_PARSE_SENSITIVE</refname>
+    <refname>SD_JSON_PARSE_MUST_BE_OBJECT</refname>
+    <refname>SD_JSON_PARSE_MUST_BE_ARRAY</refname>
+
+    <refpurpose>Parse JSON strings and files into JSON variant objects</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <funcsynopsis>
+      <funcsynopsisinfo>#include &lt;systemd/sd-json.h&gt;</funcsynopsisinfo>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse</function></funcdef>
+        <paramdef>const char *<parameter>string</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse_continue</function></funcdef>
+        <paramdef>const char **<parameter>p</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse_with_source</function></funcdef>
+        <paramdef>const char *<parameter>string</parameter></paramdef>
+        <paramdef>const char *<parameter>source</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse_with_source_continue</function></funcdef>
+        <paramdef>const char **<parameter>p</parameter></paramdef>
+        <paramdef>const char *<parameter>source</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse_file</function></funcdef>
+        <paramdef>FILE *<parameter>f</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+
+      <funcprototype>
+        <funcdef>int <function>sd_json_parse_file_at</function></funcdef>
+        <paramdef>FILE *<parameter>f</parameter></paramdef>
+        <paramdef>int <parameter>dir_fd</parameter></paramdef>
+        <paramdef>const char *<parameter>path</parameter></paramdef>
+        <paramdef>sd_json_parse_flags_t <parameter>flags</parameter></paramdef>
+        <paramdef>sd_json_variant **<parameter>ret</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_line</parameter></paramdef>
+        <paramdef>unsigned *<parameter>reterr_column</parameter></paramdef>
+      </funcprototype>
+    </funcsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Description</title>
+
+    <para><function>sd_json_parse()</function> parses the JSON string in <parameter>string</parameter> and
+    returns the resulting JSON variant object in <parameter>ret</parameter>. 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.</para>
+
+    <para>If parsing fails, the <parameter>reterr_line</parameter> and <parameter>reterr_column</parameter>
+    arguments are set to the line and column (both one-based) where the parse error occurred. One or both
+    may be passed as <constant>NULL</constant> if the caller is not interested in error location
+    information. On success, the return value is non-negative and <parameter>ret</parameter> is set to a
+    newly allocated JSON variant object (which must be freed with
+    <citerefentry><refentrytitle>sd_json_variant_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+    when no longer needed). <parameter>ret</parameter> may be passed as <constant>NULL</constant>, in which
+    case the input is validated but no object is returned.</para>
+
+    <para><function>sd_json_parse_continue()</function> is similar, but is intended for parsing a sequence of
+    concatenated JSON values from a single input string. Instead of taking a <type>const char *</type> string
+    directly, it takes a pointer to a <type>const char *</type> 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 <function>sd_json_parse()</function>, trailing content after the first JSON value is not
+    considered an error — it is expected to be the beginning of the next value.</para>
+
+    <para><function>sd_json_parse_with_source()</function> and
+    <function>sd_json_parse_with_source_continue()</function> are similar to
+    <function>sd_json_parse()</function> and <function>sd_json_parse_continue()</function>, respectively, but
+    take an additional <parameter>source</parameter> 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
+    <citerefentry><refentrytitle>sd_json_variant_get_source</refentrytitle><manvolnum>3</manvolnum></citerefentry>. If
+    <parameter>source</parameter> is <constant>NULL</constant>, no source information is
+    attached. <function>sd_json_parse()</function> and <function>sd_json_parse_continue()</function> are
+    equivalent to calling their <function>_with_source</function> counterparts with
+    <parameter>source</parameter> set to <constant>NULL</constant>.</para>
+
+    <para><function>sd_json_parse_file()</function> reads and parses a JSON value from a file. If the
+    <parameter>f</parameter> argument is non-<constant>NULL</constant>, the JSON text is read from the
+    specified <type>FILE</type> stream. If <parameter>f</parameter> is <constant>NULL</constant>, the file
+    indicated by <parameter>path</parameter> is opened and read instead. The <parameter>path</parameter>
+    argument serves a dual purpose: it is both used for opening the file (if <parameter>f</parameter> is
+    <constant>NULL</constant>) and recorded as source information in the resulting JSON variant (see
+    above).</para>
+
+    <para><function>sd_json_parse_file_at()</function> is similar to
+    <function>sd_json_parse_file()</function>, but takes an additional <parameter>dir_fd</parameter> argument
+    which specifies a file descriptor referring to the directory to resolve relative paths specified in
+    <parameter>path</parameter> against. If set to <constant>AT_FDCWD</constant>, relative paths are resolved
+    against the current working directory, which is the default behaviour of
+    <function>sd_json_parse_file()</function>.</para>
+
+    <para>The <parameter>flags</parameter> argument is a bitmask of zero or more of the following
+    flags:</para>
+
+    <variablelist>
+      <varlistentry>
+        <term><constant>SD_JSON_PARSE_SENSITIVE</constant></term>
+
+        <listitem><para>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
+        <citerefentry><refentrytitle>sd_json_variant_sensitive</refentrytitle><manvolnum>3</manvolnum></citerefentry>
+        for details.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant></term>
+
+        <listitem><para>Requires that the top-level JSON value be a JSON object (i.e. <literal>{…}</literal>).
+        If the top-level value is an array, string, number, boolean, or null, parsing fails with
+        <constant>-EINVAL</constant>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+      </varlistentry>
+
+      <varlistentry>
+        <term><constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant></term>
+
+        <listitem><para>Requires that the top-level JSON value be a JSON array (i.e. <literal>[…]</literal>).
+        If the top-level value is an object, string, number, boolean, or null, parsing fails with
+        <constant>-EINVAL</constant>.</para>
+
+        <xi:include href="version-info.xml" xpointer="v261"/></listitem>
+      </varlistentry>
+    </variablelist>
+
+    <para>If both <constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant> and
+    <constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant> are set, both objects and arrays are accepted, but
+    non-container values (strings, numbers, booleans, null) are still refused.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Return Value</title>
+
+    <para>On success, these functions return 0. On failure, they return a negative errno-style error
+    code.</para>
+
+    <refsect2>
+      <title>Errors</title>
+
+      <para>Returned errors may indicate the following problems:</para>
+
+      <variablelist>
+        <varlistentry>
+          <term><constant>-EINVAL</constant></term>
+
+          <listitem><para>The input is not valid JSON, the input contains trailing content after the parsed
+          value (only for non-<function>_continue</function> variants), or a top-level type constraint
+          specified via <constant>SD_JSON_PARSE_MUST_BE_OBJECT</constant> or
+          <constant>SD_JSON_PARSE_MUST_BE_ARRAY</constant> was violated.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENODATA</constant></term>
+
+          <listitem><para>The input string is empty or <constant>NULL</constant>.</para></listitem>
+        </varlistentry>
+
+        <varlistentry>
+          <term><constant>-ENOMEM</constant></term>
+
+          <listitem><para>Memory allocation failed.</para></listitem>
+        </varlistentry>
+      </variablelist>
+    </refsect2>
+  </refsect1>
+
+  <xi:include href="libsystemd-pkgconfig.xml" />
+
+  <refsect1>
+    <title>History</title>
+    <para><function>sd_json_parse()</function>,
+    <function>sd_json_parse_continue()</function>,
+    <function>sd_json_parse_with_source()</function>,
+    <function>sd_json_parse_with_source_continue()</function>,
+    <function>sd_json_parse_file()</function>, and
+    <function>sd_json_parse_file_at()</function> were added in version 257.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+
+    <para><simplelist type="inline">
+      <member><citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd-json</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd_json_variant_unref</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd_json_variant_get_source</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+      <member><citerefentry><refentrytitle>sd_json_dispatch</refentrytitle><manvolnum>3</manvolnum></citerefentry></member>
+    </simplelist></para>
+  </refsect1>
+</refentry>
index 0f763f75e9eba053a09daf0031b4b3c8b816a2b8..bdbd44910bfba3332393e46edbd4cf6fad85b886 100644 (file)
@@ -3,6 +3,7 @@
 #include <linux/capability.h>
 
 #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);
                 }
index 0f655a4694d9bfc063f257c48a6ba4d7cd172ee8..f41d6fef310f5c5748d9046ce31279b772be2911 100644 (file)
@@ -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));
index 02ed4dd273c6fb4fe2e4127d99486d1426da07c0..a2804e033a1de725414d68dd6a6e5541ef6614ed 100644 (file)
@@ -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.");
 
index 4c6e28500a3969a0fe4590022c317d2842eafa88..16cf910fe6d89f8a5112c7d13a1c650c78b58ecc 100644 (file)
@@ -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.");
 
index 933d18e2fd7a974903aba4a60a96b778e79ddd1d..58dc37c5bfb741b52461ac6843b1c6d4b697dfa9 100644 (file)
@@ -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");
 
index 18b0e4f37f93fea9fc959845a96710cd60b281b0..c6cfdcf6efeb831425911b22263ab6a515e0be00 100644 (file)
@@ -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");
 
index 9f11f81c4ac7b7e09f56ed3db34106a32c3b9443..723265479cc1b62b6561e2a82a4687dcc0295c70 100644 (file)
@@ -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;
 
index 507860cde62e4f7d1961b6750599309cf0739259..db4e6639d5d9a50ed73ff91236e2050e8c7f9082 100644 (file)
@@ -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, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &v, &line, &column);
+                                streq(arg_identity, "-") ? "<stdin>" : 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, "-") ? "<stdin>" : arg_identity, SD_JSON_PARSE_SENSITIVE, &json, &line, &column);
+                                streq(arg_identity, "-") ? "<stdin>" : 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");
 
index c96ecf662059b4fec899eea70c5fe715e68a63f9..f185e872955371a30126f29e9cd03f39f6f6b7fe 100644 (file)
@@ -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);
 
index 00b2e72f9fb9973ab62d8885b11a218790290f85..12e0eed2dae3295c4dc34d639a5775ec563c8ce7 100644 (file)
@@ -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);
 
index 85c92192f483df4527935044254eea4d4824990b..c9d43982d01f865a7217eb26e20132cc1790cdb7 100644 (file)
@@ -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;
index 9f7af06a4e780f1758ae65c602ec5edb54c260a1..caa05db26f491e041042f34a4e013bb0945a0ba9 100644 (file)
@@ -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.");
 
index e796f125fb56e3e6f308351d83e756c95781fa1e..2efd3ddb608fad5bea4a6789706b626f4ea1895a 100644 (file)
@@ -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);
 
index c58a3433760be223bfd2e8b3e345baeba77a1ed9..e8d7282cbf82f8f1aafc5f21d7dfc45954ae6878 100644 (file)
@@ -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");
 
index d0ceceb15584698ce4661c7969c0076ed5904b95..75a17a13aeeac6abfc04fce5c05584583d0e4db9 100644 (file)
@@ -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");
 
index cbbad44eb1e061adbd1dd6d90b1df0b208a6bb6d..0f16c65630a7e0e838015c8243e56bda4dcb511a 100644 (file)
@@ -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;
 
index 5c24de4084fb849082dc3c66c481d1d5d2475b8c..0268bbf2c29af5abd44cacadb9184edc696a8682 100644 (file)
@@ -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);
index 7829e11880643a3def209a28d604e34c59a104b4..b959fe16286ac2ebc2406eca618f345d8c29b8fa 100644 (file)
@@ -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;
 
index 7ee85e978954209d003bb2eecab1380432a4634a..c1ffaedfc80773fd70f0c1d0dc56d061989ff2e8 100644 (file)
@@ -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) {
index f7aa6f9b8f626ac8503b5087d15ac05921418c44..ec862e7d4fd21255b137b501600bc41917542002 100644 (file)
@@ -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");
 
index 2f37d5838195a109ba234705f0355d47a79209f4..d632bb62f5547e2c163e19e032798a7daff7936e 100644 (file)
@@ -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 */
index 359149ef9e0e24c984ff34da4e11b758008c4e7e..6a1977549098f65c140234161a31195009d80807 100644 (file)
@@ -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;
 
index 679152fd955a8aff70bb592ae2ed10cc9da3312a..4994d42abc43cdcbbba56593d71f784070310f4c 100644 (file)
@@ -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);
index b0d7a06941e141199204201a36946c5ac59cf4fa..8da35172c54f99088cffc6c0fdd504e36bbc1d96 100644 (file)
@@ -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 ?: "<stdin>", SD_JSON_PARSE_SENSITIVE, &v, &line, /* reterr_column= */ NULL);
+                        r = sd_json_parse_file(fn ? NULL : stdin, fn ?: "<stdin>", 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 ?: "<stdin>", line, r, "JSON parse failure.");
 
index 5048f433b17533a9c64b4494b453107dc15651bd..775a481ae8e6db29bc906c9357ff27887f47be0c 100644 (file)
@@ -682,14 +682,14 @@ static int verb_call(int argc, char *argv[], uintptr_t _data, void *userdata) {
                 source = "<argv[4]>";
 
                 /* <argv[4]> 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 = "<stdin>";
 
-                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);
index 3f65fab2cc52bc4b0fee2bcc8cb4fd3defef235b..c6e258c50af87c0743186259a49e86a1201c72f1 100644 (file)
@@ -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);