Repository: SINTEF/dlite (language: python)

Issue:
Invalid entity (json file) can crash DLite
Loading the following entity triggers an assert in jsmnx.c:

https://github.com/H2020-OpenModel/SS3_wrappers/blob/dd858a26957f522f1b60b4e255d455de842542ae/entities/fenics_materials_0.1.json



Discussion / hints:

Fix patch:
diff --git a/src/utils/jsmnx.c b/src/utils/jsmnx.c
index 2d8c1c72c..67a5e490d 100644
--- a/src/utils/jsmnx.c
+++ b/src/utils/jsmnx.c
@@ -114,12 +114,16 @@ const jsmntok_t *jsmn_item(const char *js, const jsmntok_t *t, const char *key)
 {
   int i, n, nitems;
   int len, keylen=strlen(key);
-  if (t->type != JSMN_OBJECT) return errx(1, "expected JSMN OBJECT"), NULL;
+  if (t->type != JSMN_OBJECT)
+    return errx(1, "expected JSON object in string starting with:\n%.200s\n",
+                js + t->start), NULL;
   nitems = t->size;
   for (i=0; i<nitems; i++) {
     t++;
-    assert(t->type == JSMN_STRING);
     len = t->end - t->start;
+    if (t->type != JSMN_STRING)
+      return errx(1, "invalid JSON, object key must be a string, got '%.*s'",
+                  len, js + t->start), NULL;
     if (len == keylen && strncmp(key, js + t->start, len) == 0) return t+1;
     t++;
     if ((n = jsmn_count(t)) < 0) return NULL;
@@ -139,9 +143,9 @@ const jsmntok_t *jsmn_item(const char *js, const jsmntok_t *t, const char *key)
 const jsmntok_t *jsmn_element(const char *js, const jsmntok_t *t, int i)
 {
   int j, n;
-  (void)js;  // unused
+  int len = t->end - t->start;
   if (t->type != JSMN_ARRAY)
-    return errx(1, "expected JSMN ARRAY"), NULL;
+    return errx(1, "expected JSON array, got '%.*s", len, js + t->start), NULL;
   if (i < 0 || i >= t->size)
     return errx(1, "element i=%d is out of range [0:%d]", i, t->size-1), NULL;
   for (j=0; j<i; j++) {


Test patch:
diff --git a/bindings/python/tests/input/Invalid3.json b/bindings/python/tests/input/Invalid3.json
new file mode 100644
index 000000000..cb1ed38bb
--- /dev/null
+++ b/bindings/python/tests/input/Invalid3.json
@@ -0,0 +1,19 @@
+{
+  "uri": "http://onto-ns.com/meta/0.1/Invalid3",
+  "description": "Invalid JSON (square bracket for dimensions instead of braces).",
+  "dimensions": [
+    "nfrequencies": "Number of eigen-frequencies."
+  ],
+  "properties": {
+    "name": {
+      "type": "str",
+      "description": "Name of the item."
+    },
+    "f": {
+      "type": "float64",
+      "shape": ["nf"],
+      "unit": "Hz",
+      "description": "The magic eigen-frequencies of the item."
+    }
+  }
+}
diff --git a/bindings/python/tests/test_entity.py b/bindings/python/tests/test_entity.py
index 87af26011..84c87a9bd 100755
--- a/bindings/python/tests/test_entity.py
+++ b/bindings/python/tests/test_entity.py
@@ -11,7 +11,8 @@
 
 
 thisdir = Path(__file__).absolute().parent
-dlite.storage_path.append(thisdir / "input" / "*.json")
+indir = thisdir / "input"
+dlite.storage_path.append(indir / "*.json")
 
 url = f"json://{thisdir}/MyEntity.json"
 
@@ -319,3 +320,8 @@
 Invalid2 = dlite.get_instance("http://onto-ns.com/meta/0.1/Invalid2")
 with raises(dlite.DLiteMissingInstanceError, dlite.DLiteSyntaxError):
     invalid2 = Invalid2([2])
+
+
+# For issue #691
+with raises(dlite.DLiteStorageOpenError):
+    Invalid3 = dlite.Instance.from_location("json", indir / "Invalid3.json")