diff --git a/src/plugins/e-acsl/examples/ensuresec/json_assert.c b/src/plugins/e-acsl/examples/ensuresec/json_assert.c
new file mode 100644
index 0000000000000000000000000000000000000000..95ce2721c8a3316ee7a865c1526c770789e9a2eb
--- /dev/null
+++ b/src/plugins/e-acsl/examples/ensuresec/json_assert.c
@@ -0,0 +1,296 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef E_ACSL_CONCURRENCY_PTHREAD
+#  include <pthread.h>
+#endif
+
+#include <e_acsl.h>
+
+extern int __e_acsl_sound_verdict;
+
+/*! Print to `output` the unicode string `str` as a json string, meaning all
+    unicode characters from `\x00` to `\x1f` are escaped, along with newlines,
+    tabulations, `"` and some others. */
+void json_print_escaped_str(FILE *output, const char *str) {
+  for (char *c = (char *)&str[0]; *c != '\0'; ++c) {
+    switch (*c) {
+    case '"':
+      fprintf(output, "%s", "\\\"");
+      break;
+    case '\\':
+      fprintf(output, "%s", "\\\\");
+      break;
+    case '\b':
+      fprintf(output, "%s", "\\b");
+      break;
+    case '\f':
+      fprintf(output, "%s", "\\f");
+      break;
+    case '\n':
+      fprintf(output, "%s", "\\n");
+      break;
+    case '\r':
+      fprintf(output, "%s", "\\r");
+      break;
+    case '\t':
+      fprintf(output, "%s", "\\t");
+      break;
+    default:
+      if ('\x00' <= *c && *c <= '\x1f') {
+        fprintf(output, "\\u%04x", (unsigned int)*c);
+      } else {
+        fprintf(output, "%c", *c);
+      }
+    }
+  }
+}
+
+/*! Print the given value to `output` as a json object. */
+void json_print_value(FILE *output, __e_acsl_assert_data_value_t *value) {
+  fprintf(output, "          { \"name\": \"");
+  json_print_escaped_str(output, value->name);
+  fprintf(output, "\", \"value\": \"");
+  eacsl_print_value_content(output, value);
+  fprintf(output, "\" }");
+}
+
+#ifdef E_ACSL_CONCURRENCY_PTHREAD
+/*! Global lock for accessing the output json file. */
+static pthread_mutex_t ensuresec_mutex = PTHREAD_MUTEX_INITIALIZER;
+#  define ENSURESEC_LOCK()   pthread_mutex_lock(&ensuresec_mutex)
+#  define ENSURESEC_UNLOCK() pthread_mutex_unlock(&ensuresec_mutex)
+#else
+#  define ENSURESEC_LOCK()                                                     \
+    do {                                                                       \
+    } while (0)
+#  define ENSURESEC_UNLOCK()                                                   \
+    do {                                                                       \
+    } while (0)
+#endif
+
+/*! Assert macro to output `msg` to `stderr` if `pred` is false. The output does
+    not allocate. */
+#define ENSURESEC_ASSERT(pred, msg)                                            \
+  do {                                                                         \
+    if (!(pred)) {                                                             \
+      const char m[] = msg;                                                    \
+      write(STDERR_FILENO, m, sizeof(m) - 1);                                  \
+      abort();                                                                 \
+    }                                                                          \
+  } while (0)
+
+/*! Json output file. */
+static FILE *ensuresec_output_file;
+/*! String to use for Ensuresec e-commerce ecosystem id (EE id) field. */
+static const char *ensuresec_ee_id;
+/*! String to use for Ensuresec e-commerce ecosystem tool id (EE tool id) field. */
+static const char *ensuresec_ee_tool_id;
+
+/*! \brief Finalize ensuresec output.
+
+    - Print the closing bracket of the json array.
+    - Flush the output. */
+void ensuresec_clean_assert() {
+  fprintf(ensuresec_output_file, "]\n");
+  int result = fflush(ensuresec_output_file);
+  ENSURESEC_ASSERT(result == 0, "Unable to flush ENSURESEC_OUTPUT_FILE\n");
+
+  // Purposefully do not close the json ouptut file: on normal termination, this
+  // function will be called after the return in `main()` and the shadow
+  // memories of E-ACSL will already be destroyed, resulting in a segmentation
+  // fault.
+  // The OS will automatically close the file descriptor and reclaim allocated
+  // memory on program termination.
+}
+
+/*! \brief Initialize ensuresec output.
+
+    - Retrieve the EE id and EE tool id from the environment variables
+      `ENSURESEC_EE_ID` and `ENSURESEC_EE_TOOL_ID`.
+    - Retrieve the path to the json output file from the environment variable
+      `ENSURESEC_OUTPUT_FILE`.
+    - Open the json output file.
+    - Print an opening bracket for a json array.
+    - Register the cleaning function to be called on normal program termination.
+*/
+void ensuresec_init_assert() {
+  ensuresec_ee_id = getenv("ENSURESEC_EE_ID");
+  ENSURESEC_ASSERT(ensuresec_ee_id != NULL,
+                   "Unable to retrieve env var ENSURESEC_EE_ID\n");
+
+  ensuresec_ee_tool_id = getenv("ENSURESEC_EE_TOOL_ID");
+  ENSURESEC_ASSERT(ensuresec_ee_tool_id != NULL,
+                   "Unable to retrieve env var ENSURESEC_EE_TOOL_ID\n");
+
+  const char *filename = getenv("ENSURESEC_OUTPUT_FILE");
+  ENSURESEC_ASSERT(filename != NULL,
+                   "Unable to retrieve env var ENSURESEC_OUTPUT_FILE\n");
+  if (strcmp(filename, "-") == 0) {
+    ensuresec_output_file = stdout;
+  } else {
+    ensuresec_output_file = fopen(filename, "w");
+  }
+  ENSURESEC_ASSERT(ensuresec_output_file != NULL,
+                   "Unable to open ENSURESEC_OUTPUT_FILE\n");
+
+  fprintf(ensuresec_output_file, "[\n");
+
+  // The function ensuresec_clean_assert will be called on normal program
+  // termination (via exit() or returning from main())
+  atexit(ensuresec_clean_assert);
+}
+
+/*! \brief __e_acsl_assert override to print output in a json file. */
+void __e_acsl_assert(int predicate, __e_acsl_assert_data_t *data) {
+  // Initialize output file only once per program execution
+#ifdef E_ACSL_CONCURRENCY_PTHREAD
+  static pthread_once_t already_run = PTHREAD_ONCE_INIT;
+  int result = pthread_once(&already_run, ensuresec_init_assert);
+  ENSURESEC_ASSERT(result == 0, "Unable to initialize __e_acsl_assert\n");
+#else
+  static int already_run = 0;
+  if (!already_run) {
+    ensuresec_init_assert();
+    already_run = 1;
+  }
+#endif
+
+#ifndef E_ACSL_DEBUG_ASSERT
+  if (!__e_acsl_sound_verdict || !predicate)
+#endif
+  {
+    ENSURESEC_LOCK();
+    // Id of the alert
+    static size_t alert_id = 0;
+
+    // We just want a smaller var for clearer source code
+    FILE *o = ensuresec_output_file;
+
+    // If this is not the first alert, we need to add a comma between the
+    // previous json object and the one we will print now
+    if (alert_id > 0) {
+      fprintf(o, ",\n");
+    }
+
+    // Start printing the alert
+    fprintf(o, "{\n");
+    fprintf(o, "  \"version\": \"1.0\",\n");
+    fprintf(o, "  \"partner\": \"CEA\",\n");
+    fprintf(o, "  \"id\": \"CEA/Frama-C\",\n");
+    fprintf(o, "  \"alert\": {\n");
+    fprintf(o, "    \"id\": \"%d\",\n", alert_id++);
+    fprintf(o, "    \"type\": \"%s/%s\",\n", data->kind,
+            data->name ? data->name : "");
+
+    // Print current time as an ISO8601 date
+    time_t t = time(NULL);
+    char timebuf[32]; // Enough space to print an ISO8601 date
+    strftime(timebuf, sizeof(timebuf), "%FT%T", gmtime(&t));
+    fprintf(o, "    \"startTS\": \"%s\",\n", timebuf);
+    fprintf(o, "    \"endTS\": \"%s\",\n", timebuf);
+
+    fprintf(o, "    \"eCommerceEcoId\": \"%s\",\n", ensuresec_ee_id);
+
+    // Infer the severity of the alert from its kind:
+    // - RTE index out of bound or memory access: HIGH
+    // - Other RTE: MEDIUM
+    // - Assertions from the Frama-C libc: MEDIUM
+    // - User assertions: LOW
+    static const char *severities[] = {"LOW", "MEDIUM", "HIGH"};
+    int severity_idx = 0;
+    if (strcmp(data->kind, "RTE") == 0) {
+      if (strcmp(data->name, "index_bound") == 0
+          || strcmp(data->name, "mem_access") == 0) {
+        severity_idx = 2;
+      } else {
+        severity_idx = 1;
+      }
+    } else if (strstr(data->file, "FRAMAC_SHARE/libc") != NULL) {
+      severity_idx = 1;
+    }
+
+    fprintf(o, "    \"severity\": \"%s\",\n", severities[severity_idx]);
+
+    fprintf(o, "    \"EE_Element\": [{\n");
+    fprintf(o, "      \"id\": \"%s:%d\",\n", data->file, data->line);
+    fprintf(o, "      \"elementType\": \"EEAsset\",\n");
+    fprintf(o, "      \"assetType\": \"cyber/CSourceFile\"\n");
+    fprintf(o, "    }],\n");
+
+    fprintf(o, "    \"detector\": {\n");
+    fprintf(o, "      \"type\": \"cyber/RuntimeVerification\",\n");
+    fprintf(o, "      \"providerId\": \"CEA\",\n");
+    fprintf(o, "      \"sourceId\": \"%s\"\n", ensuresec_ee_tool_id);
+    fprintf(o, "    },\n");
+    fprintf(o, "    \"data\": [{\n");
+    fprintf(o, "      \"contentType\": \"JsonData\",\n");
+    fprintf(o, "      \"type\": \"AssertionData\",\n");
+    fprintf(o, "      \"startTS\": \"%s\",\n", timebuf);
+    fprintf(o, "      \"endTS\": \"%s\",\n", timebuf);
+    fprintf(o, "      \"creationTS\": \"%s\",\n", timebuf);
+    fprintf(o, "      \"dataSource\": {\n");
+    fprintf(o, "        \"dataSourceType\": \"Detector\",\n");
+    fprintf(o, "        \"type\": \"cyber/RuntimeVerification\",\n");
+    fprintf(o, "        \"providerId\": \"CEA\",\n");
+    fprintf(o, "        \"sourceId\": \"%s\"\n", ensuresec_ee_tool_id);
+    fprintf(o, "      },\n");
+
+    // Details of the assertion
+    fprintf(o, "      \"jsonContent\": {\n");
+    // Kind of verdict
+    fprintf(o, "        \"verdict\": \"%s%s%s\",\n",
+            __e_acsl_sound_verdict ? "" : "unsound ",
+            predicate ? "valid" : "invalid",
+            data->blocking ? "" : " (non-blocking)");
+    // Kind and name of the assertion
+    fprintf(o, "        \"kind\": \"%s\",\n", data->kind);
+    fprintf(o, "        \"name\": \"%s\",\n", data->name ? data->name : "");
+    // Content of the predicate
+    fprintf(o, "        \"predicate\": \"");
+    json_print_escaped_str(o, data->pred_txt);
+    fprintf(o, "\",\n");
+    // Location in the source code
+    fprintf(o, "        \"location\": {\n");
+    fprintf(o, "          \"file\": \"%s\",\n", data->file);
+    fprintf(o, "          \"line\": \"%d\",\n", data->line);
+    fprintf(o, "          \"function\": \"%s\"\n", data->fct);
+    fprintf(o, "        },\n");
+    // Values of the assertion
+    fprintf(o, "        \"values\": [\n");
+    __e_acsl_assert_data_value_t *value = data->values;
+    while (value != NULL) {
+      json_print_value(o, value);
+      value = value->next;
+      if (value) {
+        fprintf(o, ",\n");
+      } else {
+        fprintf(o, "\n");
+      }
+    }
+    fprintf(o, "        ]\n");
+    fprintf(o, "      }\n");
+
+    // Close the alert
+    fprintf(o, "    }]\n");
+    fprintf(o, "  }\n");
+    fprintf(o, "}\n");
+    fflush(o);
+
+    if (data->blocking && __e_acsl_sound_verdict && !predicate) {
+#ifndef E_ACSL_NO_ASSERT_FAIL
+#  ifdef E_ACSL_FAIL_EXITCODE
+      exit(E_ACSL_FAIL_EXITCODE);
+#  else
+      // If we are aborting then we need to manually call the cleanup function
+      ensuresec_clean_assert();
+      abort();
+#  endif
+#endif
+    }
+
+    ENSURESEC_UNLOCK();
+  }
+}