Skip to content

Commit

Permalink
Change: Use JSON streaming parser for EPSS
Browse files Browse the repository at this point in the history
  • Loading branch information
a-h-abdelsalam committed Sep 17, 2024
1 parent 16c0a97 commit 014b635
Showing 1 changed file with 118 additions and 63 deletions.
181 changes: 118 additions & 63 deletions src/manage_sql_secinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <gvm/base/gvm_sentry.h>
#include <bsd/unistd.h>
#include <gvm/util/fileutils.h>
#include <gvm/util/jsonpull.h>
#include <gvm/util/xmlutils.h>

#undef G_LOG_DOMAIN
Expand Down Expand Up @@ -2859,121 +2860,175 @@ if (failure_condition) { \
static int
update_epss_scores ()
{
GError *error = NULL;
GStatBuf state;
gchar *current_json_path;
gchar *file_contents = NULL;
cJSON *parsed, *epss_scores_list, *list_item;
gchar *error_message = NULL;

Check warning on line 2865 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2865

Added line #L2865 was not covered by tests
FILE *epss_scores_file;
cJSON *epss_entry;
gvm_json_pull_event_t event;
gvm_json_pull_parser_t parser;
gvm_json_path_elem_t *path_tail = NULL;

Check warning on line 2870 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2870

Added line #L2870 was not covered by tests
inserts_t inserts;

current_json_path = g_build_filename (GVM_SCAP_DATA_DIR,
"epss-scores-current.json",
NULL);

if (! g_file_get_contents (current_json_path, &file_contents, NULL, &error))
if (g_stat (current_json_path, &state))
{
int ret;
if (error->code == G_FILE_ERROR_NOENT)
if (errno == ENOENT)
{
g_info ("%s: EPSS scores file '%s' not found",
__func__, current_json_path);
ret = 0;
}
else
{
g_warning ("%s: Error loading EPSS scores file: %s",
__func__, error->message);
g_warning ("%s: Failed to stat EPSS scores file: %s",

Check warning on line 2887 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2887

Added line #L2887 was not covered by tests
__func__, strerror (errno));
ret = -1;
}
g_error_free (error);
g_free (current_json_path);
return ret;
}

epss_scores_file = fopen (current_json_path, "r");

Check failure

Code scanning / CodeQL

Time-of-check time-of-use filesystem race condition High

The
filename
being operated upon was previously
checked
, but the underlying file may have been changed since then.

Check warning on line 2895 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2895

Added line #L2895 was not covered by tests
if (epss_scores_file == NULL)
{
g_warning ("%s: Failed to open EPSS scores file: %s",

Check warning on line 2898 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2898

Added line #L2898 was not covered by tests
__func__,
strerror (errno));
g_free (current_json_path);
return -1;

Check warning on line 2902 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2901-L2902

Added lines #L2901 - L2902 were not covered by tests
}

g_info ("Updating EPSS scores from %s", current_json_path);
g_free (current_json_path);

parsed = cJSON_Parse (file_contents);
g_free (file_contents);
gvm_json_pull_event_init (&event);
gvm_json_pull_parser_init (&parser, epss_scores_file);

Check warning on line 2909 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2908-L2909

Added lines #L2908 - L2909 were not covered by tests

if (parsed == NULL)
{
g_warning ("%s: EPSS scores file is not valid JSON", __func__);
return -1;
}

if (! cJSON_IsObject (parsed))
{
g_warning ("%s: EPSS scores file is not a JSON object", __func__);
cJSON_Delete (parsed);
return -1;
}
gvm_json_pull_parser_next (&parser, &event);

Check warning on line 2911 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2911

Added line #L2911 was not covered by tests

epss_scores_list = cJSON_GetObjectItem (parsed, "epss_scores");
if (epss_scores_list == NULL)
if (event.type == GVM_JSON_PULL_EVENT_OBJECT_START)
{
g_warning ("%s: Missing epss_scores field",
__func__);
cJSON_Delete (parsed);
return -1;
}
gboolean epss_scores_found = FALSE;

Check warning on line 2915 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2915

Added line #L2915 was not covered by tests
while (!epss_scores_found)
{
gvm_json_pull_parser_next (&parser, &event);
path_tail = g_queue_peek_tail (event.path);

Check warning on line 2919 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2918-L2919

Added lines #L2918 - L2919 were not covered by tests
if (event.type == GVM_JSON_PULL_EVENT_ARRAY_START
&& path_tail && strcmp (path_tail->key, "epss_scores") == 0)

Check warning on line 2921 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2921

Added line #L2921 was not covered by tests
{
epss_scores_found = TRUE;

Check warning on line 2923 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2923

Added line #L2923 was not covered by tests
}
else if (event.type == GVM_JSON_PULL_EVENT_ERROR)

Check warning on line 2925 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2925

Added line #L2925 was not covered by tests
{
g_warning ("%s: Parser error: %s", __func__, event.error_message);
gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);
return -1;

Check warning on line 2931 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2927-L2931

Added lines #L2927 - L2931 were not covered by tests
}
else if (event.type == GVM_JSON_PULL_EVENT_OBJECT_END
&& g_queue_is_empty (event.path))

Check warning on line 2934 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2933-L2934

Added lines #L2933 - L2934 were not covered by tests
{
g_warning ("%s: Unexpected json object end. Missing epss_scores field", __func__);
gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);
return -1;

Check warning on line 2940 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2936-L2940

Added lines #L2936 - L2940 were not covered by tests
}
}

sql_begin_immediate ();
inserts_init (&inserts,
sql_begin_immediate ();
inserts_init (&inserts,

Check warning on line 2945 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2944-L2945

Added lines #L2944 - L2945 were not covered by tests
EPSS_MAX_CHUNK_SIZE,
setting_secinfo_sql_buffer_threshold_bytes (),
"INSERT INTO scap2.epss_scores"
" (cve, epss, percentile)"
" VALUES ",
" ON CONFLICT (cve) DO NOTHING");

cJSON_ArrayForEach (list_item, epss_scores_list)
{
cJSON *cve_json, *epss_json, *percentile_json;

EPSS_JSON_FAIL_IF (! cJSON_IsObject (list_item),
"Unexpected non-object item in EPSS scores file")
gvm_json_pull_parser_next (&parser, &event);

Check warning on line 2953 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2953

Added line #L2953 was not covered by tests
while (event.type == GVM_JSON_PULL_EVENT_OBJECT_START)
{
cJSON *cve_json, *epss_json, *percentile_json;

cve_json = cJSON_GetObjectItem (list_item, "cve");
epss_json = cJSON_GetObjectItem (list_item, "epss");
percentile_json = cJSON_GetObjectItem (list_item, "percentile");
epss_entry = gvm_json_pull_expand_container (&parser, &error_message);

Check warning on line 2958 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2958

Added line #L2958 was not covered by tests

EPSS_JSON_FAIL_IF (cve_json == NULL,
"Item missing mandatory 'cve' field");
if (error_message)
{
g_warning ("%s: Error expanding EPSS item: %s", __func__, error_message);
g_free (error_message);
goto fail_insert;

Check warning on line 2964 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2962-L2964

Added lines #L2962 - L2964 were not covered by tests
}

EPSS_JSON_FAIL_IF (epss_json == NULL,
"Item missing mandatory 'epss' field");

EPSS_JSON_FAIL_IF (percentile_json == NULL,
"Item missing mandatory 'percentile' field");
cve_json = cJSON_GetObjectItemCaseSensitive (epss_entry, "cve");
epss_json = cJSON_GetObjectItemCaseSensitive (epss_entry, "epss");
percentile_json = cJSON_GetObjectItemCaseSensitive (epss_entry, "percentile");

Check warning on line 2969 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2967-L2969

Added lines #L2967 - L2969 were not covered by tests

EPSS_JSON_FAIL_IF (! cJSON_IsString (cve_json),
"Field 'cve' in item is not a string");
EPSS_JSON_FAIL_IF (cve_json == NULL,

Check warning on line 2971 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2971

Added line #L2971 was not covered by tests
"Item missing mandatory 'cve' field");

EPSS_JSON_FAIL_IF (! cJSON_IsNumber(epss_json),
"Field 'epss' in item is not a number");

EPSS_JSON_FAIL_IF (! cJSON_IsNumber(percentile_json),
"Field 'percentile' in item is not a number");
EPSS_JSON_FAIL_IF (epss_json == NULL,

Check warning on line 2974 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2974

Added line #L2974 was not covered by tests
"Item missing mandatory 'epss' field");

EPSS_JSON_FAIL_IF (percentile_json == NULL,

Check warning on line 2977 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2977

Added line #L2977 was not covered by tests
"Item missing mandatory 'percentile' field");

EPSS_JSON_FAIL_IF (! cJSON_IsString (cve_json),

Check warning on line 2980 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2980

Added line #L2980 was not covered by tests
"Field 'cve' in item is not a string");

EPSS_JSON_FAIL_IF (! cJSON_IsNumber(epss_json),

Check warning on line 2983 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2983

Added line #L2983 was not covered by tests
"Field 'epss' in item is not a number");

insert_epss_score_entry (&inserts,
cve_json->valuestring,
epss_json->valuedouble,
percentile_json->valuedouble);
EPSS_JSON_FAIL_IF (! cJSON_IsNumber(percentile_json),

Check warning on line 2986 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2986

Added line #L2986 was not covered by tests
"Field 'percentile' in item is not a number");

insert_epss_score_entry (&inserts,
cve_json->valuestring,

Check warning on line 2990 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2989-L2990

Added lines #L2989 - L2990 were not covered by tests
epss_json->valuedouble,
percentile_json->valuedouble);

gvm_json_pull_parser_next (&parser, &event);
cJSON_Delete (epss_entry);

Check warning on line 2995 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2994-L2995

Added lines #L2994 - L2995 were not covered by tests
}
}
else if (event.type == GVM_JSON_PULL_EVENT_ERROR)

Check warning on line 2998 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2998

Added line #L2998 was not covered by tests
{
g_warning ("%s: Parser error: %s", __func__, event.error_message);
gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);
return -1;

Check warning on line 3004 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3000-L3004

Added lines #L3000 - L3004 were not covered by tests
}
else
{
g_warning ("%s: EPSS scores file is not a JSON object.", __func__);
gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);
return -1;

Check warning on line 3012 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3008-L3012

Added lines #L3008 - L3012 were not covered by tests
}

inserts_run (&inserts, TRUE);
sql_commit ();
cJSON_Delete (parsed);

gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);

Check warning on line 3019 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3017-L3019

Added lines #L3017 - L3019 were not covered by tests
return 0;

fail_insert:
inserts_free (&inserts);
sql_rollback ();
char *printed_item = cJSON_Print (list_item);
char *printed_item = cJSON_Print (epss_entry);

Check warning on line 3025 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3025

Added line #L3025 was not covered by tests
g_message ("%s: invalid item: %s", __func__, printed_item);
cJSON_Delete (epss_entry);

Check warning on line 3027 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3027

Added line #L3027 was not covered by tests
free (printed_item);
cJSON_Delete (parsed);
gvm_json_pull_event_cleanup (&event);
gvm_json_pull_parser_cleanup (&parser);
fclose (epss_scores_file);

Check warning on line 3031 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3029-L3031

Added lines #L3029 - L3031 were not covered by tests
return -1;
}

Expand Down

0 comments on commit 014b635

Please sign in to comment.