Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change: Use JSON streaming parser for EPSS #2293

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 119 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,176 @@
static int
update_epss_scores ()
{
GError *error = NULL;
gchar *current_json_path;
gchar *file_contents = NULL;
cJSON *parsed, *epss_scores_list, *list_item;
gchar *error_message = NULL;

Check warning on line 2864 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2864

Added line #L2864 was not covered by tests
FILE *epss_scores_file;
cJSON *epss_entry;
gvm_json_pull_event_t event;
gvm_json_pull_parser_t parser;
inserts_t inserts;

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

Check warning on line 2874 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2874

Added line #L2874 was not covered by tests

if (! g_file_get_contents (current_json_path, &file_contents, NULL, &error))
if (fd < 0)
{
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 open 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);
g_free (current_json_path);

Check warning on line 2891 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2891

Added line #L2891 was not covered by tests
return ret;
}

epss_scores_file = fdopen(fd, "r");

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 convert file descriptor to 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);
close(fd);
return -1;

Check warning on line 2903 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2901-L2903

Added lines #L2901 - L2903 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 2910 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2909-L2910

Added lines #L2909 - L2910 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 2912 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2912

Added line #L2912 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 2916 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2916

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

Check warning on line 2920 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2919-L2920

Added lines #L2919 - L2920 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 2922 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2922

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

Check warning on line 2924 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2924

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

Check warning on line 2926 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2926

Added line #L2926 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 2932 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2928-L2932

Added lines #L2928 - L2932 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 2935 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2934-L2935

Added lines #L2934 - L2935 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 2941 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2937-L2941

Added lines #L2937 - L2941 were not covered by tests
}
}

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

Check warning on line 2946 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2945-L2946

Added lines #L2945 - L2946 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 2954 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2954

Added line #L2954 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 2959 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2959

Added line #L2959 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 2965 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2963-L2965

Added lines #L2963 - L2965 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 2970 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2968-L2970

Added lines #L2968 - L2970 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 2972 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2972

Added line #L2972 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 2975 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2975

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

EPSS_JSON_FAIL_IF (percentile_json == NULL,

Check warning on line 2978 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2978

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

EPSS_JSON_FAIL_IF (! cJSON_IsString (cve_json),

Check warning on line 2981 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2981

Added line #L2981 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 2984 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2984

Added line #L2984 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 2987 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2987

Added line #L2987 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 2991 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2990-L2991

Added lines #L2990 - L2991 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 2996 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2995-L2996

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

Check warning on line 2999 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L2999

Added line #L2999 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 3005 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3001-L3005

Added lines #L3001 - L3005 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 3013 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3009-L3013

Added lines #L3009 - L3013 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 3020 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3018-L3020

Added lines #L3018 - L3020 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 3026 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3026

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

Check warning on line 3028 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3028

Added line #L3028 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 3032 in src/manage_sql_secinfo.c

View check run for this annotation

Codecov / codecov/patch

src/manage_sql_secinfo.c#L3030-L3032

Added lines #L3030 - L3032 were not covered by tests
return -1;
}

Expand Down
Loading