diff --git a/SOURCES/openscap-1.3.4-bump-yamlfilter-fix-warnings-PR_1530.patch b/SOURCES/openscap-1.3.4-bump-yamlfilter-fix-warnings-PR_1530.patch new file mode 100644 index 0000000..1645dd2 --- /dev/null +++ b/SOURCES/openscap-1.3.4-bump-yamlfilter-fix-warnings-PR_1530.patch @@ -0,0 +1,59 @@ +From 31f2aa5729f9d6e9c1d8c06e3b979e89ff4e8e9e Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Tue, 19 May 2020 07:26:25 +0200 +Subject: [PATCH 1/3] Update yaml-filter to the latest version (fixes minor + warnings) + +yaml-path.c:342:61: warning: comparison of integer expressions of different signedness: 'int' and 'size_t' {aka 'long unsigned int'} [-Wsign-compare] +yaml-path.c:251:27: warning: unused variable 'sec' [-Wunused-variable] +--- + CMakeLists.txt | 4 ++-- + yaml-filter | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 65d674140..8752d66c8 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -545,7 +545,7 @@ include_directories( + "src/XCCDF/public/" + "src/XCCDF_POLICY/" + "src/XCCDF_POLICY/public/" +- "yaml-filter" ++ "yaml-filter/src/" + ${CMAKE_BINARY_DIR} # config.h is generated to build directory + ${LIBXML2_INCLUDE_DIR} + ${PCRE_INCLUDE_DIRS} +@@ -570,7 +570,7 @@ function(set_oscap_generic_properties TARGET_OBJECT) + endfunction() + + if(OPENSCAP_PROBE_INDEPENDENT_YAMLFILECONTENT) +- add_library(yamlfilter_object OBJECT yaml-filter/yaml-path.c yaml-filter/yaml-path.h) ++ add_library(yamlfilter_object OBJECT yaml-filter/src/yaml-path.c yaml-filter/src/yaml-path.h) + set_oscap_generic_properties(yamlfilter_object) + endif() + + +From 69111f40e24a44241609f485034420bac666e756 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Tue, 19 May 2020 07:28:53 +0200 +Subject: [PATCH 2/3] probes/yamlfilecontent: Properly destroy yaml_path before + bailing out + +yamlfilecontent_probe.c:163: leaked_storage: Variable "yaml_path" going out of scope leaks the storage it points to. +--- + src/OVAL/probes/independent/yamlfilecontent_probe.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 8fc4b32b2..b8a379313 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -159,6 +159,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); + SEXP_free(msg); + probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); ++ yaml_path_destroy(yaml_path); + fclose(yaml_file); + return -1; + }; diff --git a/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-PR_1552.patch b/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-PR_1552.patch new file mode 100644 index 0000000..a80bb00 --- /dev/null +++ b/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-PR_1552.patch @@ -0,0 +1,1611 @@ +From b8dcde269fb97a021b44b2c54a823a50f3d8982c Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Wed, 15 Jul 2020 14:46:06 +0200 +Subject: [PATCH 1/8] OVAL: Fix evaluation of field entries in records + +Add proper evaluation implementation for operation, var_check +and var_ref attributes in the field entry of a record. +--- + src/OVAL/results/oval_cmp.c | 6 ---- + src/OVAL/results/oval_cmp_impl.h | 10 ------- + src/OVAL/results/oval_resultTest.c | 44 +++++++++++++++++++----------- + 3 files changed, 28 insertions(+), 32 deletions(-) + +diff --git a/src/OVAL/results/oval_cmp.c b/src/OVAL/results/oval_cmp.c +index 39e1f59e5..585332223 100644 +--- a/src/OVAL/results/oval_cmp.c ++++ b/src/OVAL/results/oval_cmp.c +@@ -161,9 +161,3 @@ oval_result_t oval_str_cmp_str(char *state_data, oval_datatype_t state_data_type + oscap_seterr(OSCAP_EFAMILY_OVAL, "Invalid OVAL data type: %d.", state_data_type); + return OVAL_RESULT_ERROR; + } +- +-oval_result_t oval_ent_cmp_str(char *state_data, oval_datatype_t state_data_type, struct oval_sysent *sysent, oval_operation_t operation) +-{ +- const char *sys_data = oval_sysent_get_value(sysent); +- return oval_str_cmp_str(state_data, state_data_type, sys_data, operation); +-} +diff --git a/src/OVAL/results/oval_cmp_impl.h b/src/OVAL/results/oval_cmp_impl.h +index 4e38810f5..01014763b 100644 +--- a/src/OVAL/results/oval_cmp_impl.h ++++ b/src/OVAL/results/oval_cmp_impl.h +@@ -29,16 +29,6 @@ + #include "oval_system_characteristics.h" + + +-/** +- * Compare state entity (or variable/value) to sysent object collected from system. +- * This function does not support @datatype="record". +- * @param state_value Value defined within state/entity/value or variable/value +- * @param sysent Value collected from system +- * @operation Comparison type operation +- * @returns OVAL Result of comparison +- */ +-oval_result_t oval_ent_cmp_str(char *state_data, oval_datatype_t state_data_type, struct oval_sysent *sysent, oval_operation_t operation); +- + /** + * Compare state entity (or variable/value) to data collected from system. + * This function does not support @datatype="record". +diff --git a/src/OVAL/results/oval_resultTest.c b/src/OVAL/results/oval_resultTest.c +index b52b845ac..38477cb3b 100644 +--- a/src/OVAL/results/oval_resultTest.c ++++ b/src/OVAL/results/oval_resultTest.c +@@ -381,17 +381,11 @@ oval_result_t ores_get_result_byopr(struct oresults *ores, oval_operator_t op) + return result; + } + +-static inline oval_result_t _evaluate_sysent_with_variable(struct oval_syschar_model *syschar_model, struct oval_entity *state_entity, struct oval_sysent *item_entity, oval_operation_t state_entity_operation, struct oval_state_content *content) ++static inline oval_result_t _evaluate_sysent_with_variable(struct oval_syschar_model *syschar_model, struct oval_variable *state_entity_var, const char *sys_data, oval_operation_t state_entity_operation, oval_check_t var_check) + { + oval_syschar_collection_flag_t flag; + oval_result_t ent_val_res; + +- struct oval_variable *state_entity_var; +- if ((state_entity_var = oval_entity_get_variable(state_entity)) == NULL) { +- oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL variable"); +- return -1; +- } +- + if (0 != oval_syschar_model_compute_variable(syschar_model, state_entity_var)) { + return -1; + } +@@ -420,16 +414,15 @@ static inline oval_result_t _evaluate_sysent_with_variable(struct oval_syschar_m + } + oval_datatype_t state_entity_val_datatype = oval_value_get_datatype(var_val); + +- var_val_res = oval_ent_cmp_str(state_entity_val_text, state_entity_val_datatype, item_entity, state_entity_operation); ++ var_val_res = oval_str_cmp_str(state_entity_val_text, state_entity_val_datatype, sys_data, state_entity_operation); + if (var_val_res == OVAL_RESULT_ERROR) { + dE("Error occured when comparing a variable '%s' value '%s' with collected item entity = '%s'", +- oval_variable_get_id(state_entity_var), state_entity_val_text, oval_sysent_get_value(item_entity)); ++ oval_variable_get_id(state_entity_var), state_entity_val_text, sys_data); + } + ores_add_res(&var_ores, var_val_res); + } + oval_value_iterator_free(val_itr); + +- oval_check_t var_check = oval_state_content_get_var_check(content); + ent_val_res = ores_get_result_bychk(&var_ores, var_check); + } break; + case SYSCHAR_FLAG_ERROR: +@@ -450,6 +443,9 @@ struct record_field_instance { + char *value; + oval_datatype_t data_type; + oval_check_t ent_check; ++ oval_operation_t operation; ++ struct oval_variable *var; ++ oval_check_t var_check; + }; + + static struct record_field_instance _oval_record_field_iterator_next_instance(struct oval_record_field_iterator *iterator) +@@ -461,11 +457,14 @@ static struct record_field_instance _oval_record_field_iterator_next_instance(st + instance.data_type = oval_record_field_get_datatype(rf); + if (oval_record_field_get_type(rf) == OVAL_RECORD_FIELD_STATE) { + instance.ent_check = oval_record_field_get_ent_check(rf); ++ instance.operation = oval_record_field_get_operation(rf); + } ++ instance.var = oval_record_field_get_variable(rf); ++ instance.var_check = oval_record_field_get_var_check(rf); + return instance; + } + +-static oval_result_t _evaluate_sysent_record(struct oval_state_content *state_content, struct oval_sysent *item_entity) ++static oval_result_t _evaluate_sysent_record(struct oval_syschar_model *syschar_model, struct oval_state_content *state_content, struct oval_sysent *item_entity) + { + struct oresults record_ores; + ores_clear(&record_ores); +@@ -485,7 +484,12 @@ static oval_result_t _evaluate_sysent_record(struct oval_state_content *state_co + struct record_field_instance item_rf = _oval_record_field_iterator_next_instance(item_it); + if (strcmp(state_rf.name, item_rf.name) == 0) { + field_found = true; +- oval_result_t fields_comparison_result = oval_str_cmp_str(state_rf.value, state_rf.data_type, item_rf.value, OVAL_OPERATION_EQUALS); ++ oval_result_t fields_comparison_result; ++ if (state_rf.var != NULL) { ++ fields_comparison_result = _evaluate_sysent_with_variable(syschar_model, state_rf.var, item_rf.value, state_rf.operation, state_rf.var_check); ++ } else { ++ fields_comparison_result = oval_str_cmp_str(state_rf.value, state_rf.data_type, item_rf.value, state_rf.operation); ++ } + ores_add_res(&field_ores, fields_comparison_result); + } + } +@@ -510,10 +514,17 @@ static inline oval_result_t _evaluate_sysent(struct oval_syschar_model *syschar_ + if (oval_sysent_get_status(item_entity) == SYSCHAR_STATUS_DOES_NOT_EXIST) { + return OVAL_RESULT_FALSE; + } else if (oval_entity_get_varref_type(state_entity) == OVAL_ENTITY_VARREF_ATTRIBUTE) { ++ struct oval_variable *state_entity_var; ++ if ((state_entity_var = oval_entity_get_variable(state_entity)) == NULL) { ++ oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL variable"); ++ return -1; ++ } ++ const char *sys_data = oval_sysent_get_value(item_entity); ++ oval_check_t var_check = oval_state_content_get_var_check(content); + + return _evaluate_sysent_with_variable(syschar_model, +- state_entity, item_entity, +- state_entity_operation, content); ++ state_entity_var, sys_data, ++ state_entity_operation, var_check); + } else { + struct oval_value *state_entity_val; + char *state_entity_val_text; +@@ -525,7 +536,7 @@ static inline oval_result_t _evaluate_sysent(struct oval_syschar_model *syschar_ + dE("The only allowed operation for comparing record types is 'equals'."); + return OVAL_RESULT_ERROR; + } +- return _evaluate_sysent_record(content, item_entity); ++ return _evaluate_sysent_record(syschar_model, content, item_entity); + } else { + if ((state_entity_val = oval_entity_get_value(state_entity)) == NULL) { + oscap_seterr(OSCAP_EFAMILY_OVAL, "OVAL internal error: found NULL entity value"); +@@ -537,7 +548,8 @@ static inline oval_result_t _evaluate_sysent(struct oval_syschar_model *syschar_ + } + state_entity_val_datatype = oval_value_get_datatype(state_entity_val); + +- return oval_ent_cmp_str(state_entity_val_text, state_entity_val_datatype, item_entity, state_entity_operation); ++ const char *sys_data = oval_sysent_get_value(item_entity); ++ return oval_str_cmp_str(state_entity_val_text, state_entity_val_datatype, sys_data, state_entity_operation); + } + } + } + +From 21d52f1eb56951be976674f318ecfd5359f282c6 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Thu, 25 Jun 2020 23:31:11 +0200 +Subject: [PATCH 2/8] probes/yamlfilecontent: Bump yaml-filter, extend the + schema and probe to be able to work with a set of values in maps + +This introduces YAML Path selection ($.blah['key1','key2']) syntax and +enriches yamlfilecontent test and schema with EntityStateRecordType and +EntityItemRecordType result elements for collecting and checking +complex objects in YAML/JSON documents. + +This change also depend on removal of the 'only lower case' restriction +from the name attribute of the field element (EntityXxxFieldType). +--- + .../5.11.3/independent-definitions-schema.xsd | 19 ++- + ...ependent-system-characteristics-schema.xsd | 13 +- + .../oval/5.11.3/oval-definitions-schema.xsd | 14 +- + .../oval-system-characteristics-schema.xsd | 7 +- + .../independent/yamlfilecontent_probe.c | 155 ++++++++++++------ + .../yamlfilecontent/openshift-logging.yaml | 28 ++++ + .../test_probes_yamlfilecontent_array.xml | 8 +- + .../test_probes_yamlfilecontent_key.sh | 2 +- + .../test_probes_yamlfilecontent_key.xml | 70 +++++++- + ...st_probes_yamlfilecontent_offline_mode.xml | 12 +- + .../test_probes_yamlfilecontent_types.sh | 40 ++--- + yaml-filter | 2 +- + 12 files changed, 261 insertions(+), 109 deletions(-) + +diff --git a/schemas/oval/5.11.3/independent-definitions-schema.xsd b/schemas/oval/5.11.3/independent-definitions-schema.xsd +index 53e67e187..0f202abf6 100644 +--- a/schemas/oval/5.11.3/independent-definitions-schema.xsd ++++ b/schemas/oval/5.11.3/independent-definitions-schema.xsd +@@ -2115,7 +2115,7 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list of zero or more scalar values which will be accessible in OVAL via instances of the value_of entity. Any results from evaluating the YAML Path expression other than a list of scalar values (e.g., a hash or list of lists) is considered an error. The intention is that the scalar values be drawn from instances of a single, uniquely named element. However, an OVAL interpreter is not required to verify this, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. + + + +@@ -2158,13 +2158,24 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list of zero or more text values which will be accessible in OVAL via instances of the value_of entity. Any results from evaluating the YAML Path expression other than a list of text strings (e.g., a nodes set) is considered an error. The intention is that the text values be drawn from instances of a single, uniquely named element or attribute. However, an OVAL interpreter is not required to verify this, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. + + +- ++ + +- The value_of element checks the value(s) of the text node(s) or attribute(s) found. ++ The value entity specifies how to test objects in the value set of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values, set name attribute of the field child element to an empty string (''). The check is entirely controlled by operator attributes of the field element. ++ ++ ++ ++ - datatype attribute for the value entity of a yamlfile_state must be 'record' ++ ++ ++ + ++ ++ ++ ++ + + + +diff --git a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +index 19c6a240e..b2934d4c6 100644 +--- a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd ++++ b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +@@ -606,12 +606,19 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list of zero or more scalar values which will be accessible in OVAL via instances of the value_of entity. Any results from evaluating the YAML Path expression other than a list of scalar values (e.g., a hash or list of lists) is considered an error. The intention is that the scalar values be drawn from instances of a single, uniquely named element. However, an OVAL interpreter is not required to verify this, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. + + +- ++ + +- The value_of element checks the value(s) of the text node(s) or attribute(s) found. How this is used is entirely controlled by operator attributes. ++ The value entity holds the target(s) of the specified YAML Path. If YAML Path target is a signle scalar value or a list of scalar values, the field child element's name would be empty (''). The check is entirely controlled by operator attributes of the field element. ++ ++ ++ ++ - datatype attribute for the value entity of a yamlfile_item must be 'record' ++ ++ ++ + + + +diff --git a/schemas/oval/5.11.3/oval-definitions-schema.xsd b/schemas/oval/5.11.3/oval-definitions-schema.xsd +index f43abd318..a57b889cf 100644 +--- a/schemas/oval/5.11.3/oval-definitions-schema.xsd ++++ b/schemas/oval/5.11.3/oval-definitions-schema.xsd +@@ -1553,13 +1553,8 @@ + + + +- A string restricted to disallow upper case characters. ++ A string. + +- +- +- +- +- + + + +@@ -1824,13 +1819,8 @@ + + + +- A string restricted to disallow upper case characters. ++ A string. + +- +- +- +- +- + + + +diff --git a/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd b/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd +index 30962f330..543b2db71 100644 +--- a/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd ++++ b/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd +@@ -574,13 +574,8 @@ + + + +- A string restricted to disallow upper case characters. ++ A string. + +- +- +- +- +- + + + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 66d08ad22..f7c7d08cb 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -73,7 +73,7 @@ static SEXP_t *yaml_scalar_event_to_sexp(yaml_event_t *event) + char *tag = (char *) event->data.scalar.tag; + char *value = (char *) event->data.scalar.value; + +- /* nodes lacking an explicit tag are given a non-specific tag: ++ /* Nodes lacking an explicit tag are given a non-specific tag: + * “!” for non-plain scalars, and “?” for all other nodes + */ + if (tag == NULL) { +@@ -139,30 +139,30 @@ static SEXP_t *yaml_scalar_event_to_sexp(yaml_event_t *event) + return SEXP_string_new(value, strlen(value)); + } + ++#define result_error(fmt, args...) \ ++do { \ ++ SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, fmt, args); \ ++ probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); \ ++ SEXP_free(msg); \ ++ probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); \ ++ ret = -1; \ ++} while (0) ++ + static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, struct oscap_list *values, probe_ctx *ctx) + { + int ret = 0; + FILE *yaml_file = fopen(filepath, "r"); + if (yaml_file == NULL) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "Unable to open file '%s': %s", filepath, strerror(errno)); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); +- return -1; ++ result_error("Unable to open file '%s': %s", filepath, strerror(errno)); ++ return ret; + } + + yaml_path_t *yaml_path = yaml_path_create(); + if (yaml_path_parse(yaml_path, (char *) yaml_path_cstr)) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "Invalid YAML path '%s' (%s)\n", yaml_path_cstr, +- yaml_path_error_get(yaml_path)->message); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); ++ result_error("Invalid YAML path '%s': %s", yaml_path_cstr, yaml_path_error_get(yaml_path)->message); + yaml_path_destroy(yaml_path); + fclose(yaml_file); +- return -1; ++ return ret; + }; + + yaml_parser_t parser; +@@ -172,71 +172,94 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + yaml_event_t event; + yaml_event_type_t event_type; + bool sequence = false; ++ bool mapping = false; ++ int index = 0; ++ char *key = strdup(""); ++ ++ struct oscap_htable *record = NULL; + + do { + if (!yaml_parser_parse(&parser, &event)) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "YAML parser error: yaml_parse_parse returned 0: %s", +- parser.problem); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); +- ret = -1; ++ result_error("YAML parser error: %s", parser.problem); + goto cleanup; + } +- +- event_type = event.type; + if (!yaml_path_filter_event(yaml_path, &parser, &event, + YAML_PATH_FILTER_RETURN_ALL)) { + goto next; + } ++ ++ event_type = event.type; ++ + if (sequence) { + if (event_type == YAML_SEQUENCE_END_EVENT) { + sequence = false; +- } else if (event_type != YAML_SCALAR_EVENT) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "YAML path '%s' contains non-scalar in a sequence.", +- yaml_path_cstr); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); +- ret = -1; ++ } else if (event_type == YAML_SEQUENCE_START_EVENT) { ++ result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); + goto cleanup; + } + } else { + if (event_type == YAML_SEQUENCE_START_EVENT) { + sequence = true; + } +- if (event_type == YAML_MAPPING_START_EVENT) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "YAML path '%s' matches a mapping.", +- yaml_path_cstr); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); +- ret = -1; ++ } ++ ++ if (mapping) { ++ if (event_type == YAML_MAPPING_END_EVENT) { ++ mapping = false; ++ oscap_list_add(values, record); ++ record = NULL; ++ } else if (event_type == YAML_MAPPING_START_EVENT) { ++ result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); + goto cleanup; + } ++ } else { ++ if (event_type == YAML_MAPPING_START_EVENT) { ++ if (record) { ++ result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); ++ goto cleanup; ++ } ++ mapping = true; ++ sequence = false; ++ index = 0; ++ record = oscap_htable_new(); ++ } + } ++ + if (event_type == YAML_SCALAR_EVENT) { ++ if (mapping) { ++ if (!sequence) { ++ if (index++ % 2 == 0) { ++ free(key); ++ key = strdup((const char *) event.data.scalar.value); ++ goto next; ++ } ++ } ++ } ++ + SEXP_t *sexp = yaml_scalar_event_to_sexp(&event); + if (sexp == NULL) { +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, +- "Can't convert '%s %s' to SEXP", event.data.scalar.tag, +- event.data.scalar.value); +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); +- SEXP_free(msg); +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); +- ret = -1; ++ result_error("Can't convert '%s %s' to SEXP", event.data.scalar.tag, event.data.scalar.value); + goto cleanup; + } +- oscap_list_add(values, sexp); ++ ++ if (!record) ++ record = oscap_htable_new(); ++ struct oscap_list *field = oscap_htable_get(record, key); ++ if (!field) { ++ field = oscap_list_new(); ++ oscap_htable_add(record, key, field); ++ } ++ ++ oscap_list_add(field, sexp); + } + next: + yaml_event_delete(&event); + } while (event_type != YAML_STREAM_END_EVENT); + + cleanup: ++ if (record) ++ oscap_list_add(values, record); ++ free(key); + yaml_parser_delete(&parser); + yaml_path_destroy(yaml_path); + fclose(yaml_file); +@@ -244,6 +267,16 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + return ret; + } + ++static void record_free(struct oscap_list *items) ++{ ++ oscap_list_free(items, (oscap_destruct_func) SEXP_free); ++} ++ ++static void values_free(struct oscap_htable *record) ++{ ++ oscap_htable_free(record, (oscap_destruct_func) record_free); ++} ++ + static int process_yaml_file(const char *prefix, const char *path, const char *filename, const char *yamlpath, probe_ctx *ctx) + { + int ret = 0; +@@ -265,21 +298,37 @@ static int process_yaml_file(const char *prefix, const char *path, const char *f + "path", OVAL_DATATYPE_STRING, path, + "filename", OVAL_DATATYPE_STRING, filename, + "yamlpath", OVAL_DATATYPE_STRING, yamlpath, +- /* +- "windows_view", +- */ ++ // TODO: Implement "windows_view", + NULL + ); + while (oscap_iterator_has_more(values_it)) { +- SEXP_t *value_sexp = oscap_iterator_next(values_it); +- probe_item_ent_add(item, "value_of", NULL, value_sexp); ++ SEXP_t *result_ent = probe_ent_creat1("value", NULL, NULL); ++ probe_ent_setdatatype(result_ent, OVAL_DATATYPE_RECORD); ++ struct oscap_htable *record = oscap_iterator_next(values_it); ++ struct oscap_htable_iterator *record_it = oscap_htable_iterator_new(record); ++ while(oscap_htable_iterator_has_more(record_it)) { ++ const struct oscap_htable_item *record_item = oscap_htable_iterator_next(record_it); ++ struct oscap_iterator *item_value_it = oscap_iterator_new(record_item->value); ++ SEXP_t se_tmp_mem; ++ SEXP_t *key = SEXP_string_new_r(&se_tmp_mem, record_item->key, strlen(record_item->key)); ++ while(oscap_iterator_has_more(item_value_it)) { ++ SEXP_t *value_sexp = oscap_iterator_next(item_value_it); ++ SEXP_t *field = probe_ent_creat1("field", NULL, value_sexp); ++ probe_item_attr_add(field, "name", key); ++ SEXP_list_add(result_ent, field); ++ } ++ oscap_iterator_free(item_value_it); ++ SEXP_free_r(&se_tmp_mem); ++ } ++ oscap_htable_iterator_free(record_it); ++ SEXP_list_add(item, result_ent); + } + probe_item_collect(ctx, item); + } + oscap_iterator_free(values_it); + + cleanup: +- oscap_list_free(values, (oscap_destruct_func) SEXP_free); ++ oscap_list_free(values, (oscap_destruct_func) values_free); + free(filepath_with_prefix); + free(filepath); + return ret; +diff --git a/tests/probes/yamlfilecontent/openshift-logging.yaml b/tests/probes/yamlfilecontent/openshift-logging.yaml +index a89e70942..fb6a9d8b6 100644 +--- a/tests/probes/yamlfilecontent/openshift-logging.yaml ++++ b/tests/probes/yamlfilecontent/openshift-logging.yaml +@@ -38,3 +38,31 @@ spec: + inputSource: logs.audit + outputRefs: + - secureforward-offcluster ++status: ++ conditions: ++ - lastTransitionTime: "2020-06-08T04:54:58Z" ++ reason: AsExpected ++ status: "False" ++ type: Degraded ++ - lastTransitionTime: "2020-06-08T06:34:00Z" ++ reason: AsExpected ++ status: "False" ++ type: Progressing ++ - lastTransitionTime: "2020-06-08T04:51:08Z" ++ reason: AsExpected ++ status: "True" ++ type: Available ++ - lastTransitionTime: "2020-06-08T04:45:45Z" ++ reason: AsExpected ++ status: "True" ++ type: Upgradeable ++ extension: null ++ relatedObjects: ++ - group: operator.openshift.io ++ name: cluster ++ resource: openshiftapiservers ++ versions: ++ - name: operator ++ version: 4.5.0-0.nightly-2020-06-04-214605 ++ - name: openshift-apiserver ++ version: 4.5.0-0.nightly-2020-06-04-214605 +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +index 768b0b4a3..9a4227fb8 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +@@ -76,11 +76,15 @@ + + + +- secureforward-offcluster ++ ++ secureforward-offcluster ++ + + + +- ++ ++ ++ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +index 300c1b496..b25ad99f9 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +@@ -19,7 +19,7 @@ function test_probes_yamlfilecontent_key { + $OSCAP oval eval --results $RF $DF + + if [ -f $RF ]; then +- verify_results "def" $DF $RF 3 && verify_results "tst" $DF $RF 4 ++ verify_results "def" $DF $RF 5 && verify_results "tst" $DF $RF 6 + ret_val=$? + else + ret_val=1 +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +index f055bb648..fc3ee939e 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +@@ -21,7 +21,7 @@ + + + +- ++ + + + +@@ -41,6 +41,26 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -55,7 +75,7 @@ + + + +- ++ + + + +@@ -63,6 +83,16 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -91,16 +121,48 @@ + .doesnt.exist + + ++ ++ /tmp ++ openshift-logging.yaml ++ .status.conditions[:]['status','type'] ++ ++ ++ ++ /tmp ++ openshift-logging.yaml ++ .status.conditions[:] ++ ++ + + + + + +- LogForwarding ++ ++ LogForwarding ++ + + + +- openshift-logging ++ ++ openshift-logging ++ ++ ++ ++ ++ ++ True ++ Upgradeable ++ ++ ++ ++ ++ ++ True ++ AsExpected ++ Upgradeable ++ ^\d+-\d+-.*Z$ ++ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml +index 675f528e8..5c7cf5b28 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml +@@ -85,15 +85,21 @@ + + + +- instance ++ ++ instance ++ + + + +- outstance ++ ++ outstance ++ + + + +- instance ++ ++ instance ++ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +index 97bb33dfd..8b0614fc6 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +@@ -24,49 +24,49 @@ function test_probes_yamlfilecontent_types { + + sd='/oval_results/results/system/oval_system_characteristics/system_data' + +- assert_exists 8 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="boolean"]' +- assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="boolean" and text()="true"]' +- assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="boolean" and text()="false"]' ++ assert_exists 8 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean"]' ++ assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean" and text()="true"]' ++ assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean" and text()="false"]' + +- assert_exists 5 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int"]' ++ assert_exists 5 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int"]' + # int_10: 42 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int" and text()="42"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="42"]' + # int_10_neg: -17 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int" and text()="-17"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="-17"]' + # int_8: 0o33 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int" and text()="27"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="27"]' + # int_16: 0xFF +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int" and text()="255"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="255"]' + # int_cast: !!int "369" +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="int" and text()="369"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="369"]' + +- assert_exists 7 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float"]' ++ assert_exists 7 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float"]' + # float: 7.4 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="7.400000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="7.400000"]' + # float_neg: -0.3 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="-0.300000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="-0.300000"]' + # float_exp: +12e03 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="12000.000000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="12000.000000"]' + # float_exp_neg: -43e-4 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="-0.004300"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="-0.004300"]' + # float: .inf +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="inf"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="inf"]' + # float: .NAN +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="nan"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="nan"]' + # float_cast: !!float "978.65" +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype="float" and text()="978.650000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="978.650000"]' + + # string_true +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype!="boolean" and text()="true"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype!="boolean" and text()="true"]' + # string_number +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value_of[@datatype!="int" and text()="81"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype!="int" and text()="81"]' + + # bool_error_cast, int_error_cast, float_error_cast + co='/oval_results/results/system/oval_system_characteristics/collected_objects' + assert_exists 3 $co'/object[@flag="error"]' + assert_exists 3 $co'/object[@flag="error"]/message' + +- rm -f $result ++ #rm -f $result + rm -f $YAML_FILE + + } + +From 4f129b7142360ff4adb06aa49ee050ccf335fa8e Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Fri, 31 Jul 2020 08:37:25 +0200 +Subject: [PATCH 3/8] OVAL: Fix record field evaluation + +--- + src/OVAL/oval_recordField.c | 20 ++++++++++---------- + src/OVAL/results/oval_resultTest.c | 4 ++-- + 2 files changed, 12 insertions(+), 12 deletions(-) + +diff --git a/src/OVAL/oval_recordField.c b/src/OVAL/oval_recordField.c +index a0c8a6a54..dd765097d 100644 +--- a/src/OVAL/oval_recordField.c ++++ b/src/OVAL/oval_recordField.c +@@ -208,7 +208,7 @@ void oval_record_field_set_mask(struct oval_record_field *rf, int mask) + void oval_record_field_set_operation(struct oval_record_field *rf, oval_operation_t operation) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (set operation): %d.", rf->record_field_type); + return; + } + +@@ -218,7 +218,7 @@ void oval_record_field_set_operation(struct oval_record_field *rf, oval_operatio + void oval_record_field_set_variable(struct oval_record_field *rf, struct oval_variable *var) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (set variable): %d.", rf->record_field_type); + return; + } + +@@ -228,7 +228,7 @@ void oval_record_field_set_variable(struct oval_record_field *rf, struct oval_va + void oval_record_field_set_var_check(struct oval_record_field *rf, oval_check_t var_check) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (set var_check): %d.", rf->record_field_type); + return; + } + +@@ -238,7 +238,7 @@ void oval_record_field_set_var_check(struct oval_record_field *rf, oval_check_t + void oval_record_field_set_ent_check(struct oval_record_field *rf, oval_check_t ent_check) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (set ent_check): %d.", rf->record_field_type); + return; + } + +@@ -248,7 +248,7 @@ void oval_record_field_set_ent_check(struct oval_record_field *rf, oval_check_t + void oval_record_field_set_status(struct oval_record_field *rf, oval_syschar_status_t status) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_ITEM) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (set status): %d.", rf->record_field_type); + return; + } + +@@ -283,7 +283,7 @@ int oval_record_field_get_mask(struct oval_record_field *rf) + oval_operation_t oval_record_field_get_operation(struct oval_record_field *rf) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (get operation): %d.", rf->record_field_type); + return OVAL_OPERATION_UNKNOWN; + } + +@@ -293,7 +293,7 @@ oval_operation_t oval_record_field_get_operation(struct oval_record_field *rf) + struct oval_variable *oval_record_field_get_variable(struct oval_record_field *rf) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (get variable): %d.", rf->record_field_type); + return NULL; + } + +@@ -303,7 +303,7 @@ struct oval_variable *oval_record_field_get_variable(struct oval_record_field *r + oval_check_t oval_record_field_get_var_check(struct oval_record_field *rf) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (get var_check): %d.", rf->record_field_type); + return OVAL_CHECK_UNKNOWN; + } + +@@ -313,7 +313,7 @@ oval_check_t oval_record_field_get_var_check(struct oval_record_field *rf) + oval_check_t oval_record_field_get_ent_check(struct oval_record_field *rf) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_STATE) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (get ent_check): %d.", rf->record_field_type); + return OVAL_CHECK_UNKNOWN; + } + +@@ -323,7 +323,7 @@ oval_check_t oval_record_field_get_ent_check(struct oval_record_field *rf) + oval_syschar_status_t oval_record_field_get_status(struct oval_record_field *rf) + { + if (rf->record_field_type != OVAL_RECORD_FIELD_ITEM) { +- dE("Wrong record field type: %d.", rf->record_field_type); ++ dE("Wrong record field type (get status): %d.", rf->record_field_type); + return SYSCHAR_STATUS_UNKNOWN; + } + +diff --git a/src/OVAL/results/oval_resultTest.c b/src/OVAL/results/oval_resultTest.c +index 38477cb3b..00e161481 100644 +--- a/src/OVAL/results/oval_resultTest.c ++++ b/src/OVAL/results/oval_resultTest.c +@@ -458,9 +458,9 @@ static struct record_field_instance _oval_record_field_iterator_next_instance(st + if (oval_record_field_get_type(rf) == OVAL_RECORD_FIELD_STATE) { + instance.ent_check = oval_record_field_get_ent_check(rf); + instance.operation = oval_record_field_get_operation(rf); ++ instance.var = oval_record_field_get_variable(rf); ++ instance.var_check = oval_record_field_get_var_check(rf); + } +- instance.var = oval_record_field_get_variable(rf); +- instance.var_check = oval_record_field_get_var_check(rf); + return instance; + } + + +From 277548a18c4d7312df4e19da5cff38dfff56fd45 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Fri, 31 Jul 2020 08:38:10 +0200 +Subject: [PATCH 4/8] probes/yamlfilecontent: Avoid collecting empty records + +--- + .../independent/yamlfilecontent_probe.c | 7 ++++++- + .../test_probes_yamlfilecontent_key.sh | 2 +- + .../test_probes_yamlfilecontent_key.xml | 20 +++++++++++++++++++ + .../test_probes_yamlfilecontent_types.sh | 2 -- + 4 files changed, 27 insertions(+), 4 deletions(-) + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index f7c7d08cb..8e276dc3c 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -206,7 +206,12 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + if (mapping) { + if (event_type == YAML_MAPPING_END_EVENT) { + mapping = false; +- oscap_list_add(values, record); ++ if (record->itemcount > 0) { ++ oscap_list_add(values, record); ++ } else { ++ // Do not collect empty records ++ oscap_htable_free0(record); ++ } + record = NULL; + } else if (event_type == YAML_MAPPING_START_EVENT) { + result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +index b25ad99f9..fc1e0ae7e 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +@@ -19,7 +19,7 @@ function test_probes_yamlfilecontent_key { + $OSCAP oval eval --results $RF $DF + + if [ -f $RF ]; then +- verify_results "def" $DF $RF 5 && verify_results "tst" $DF $RF 6 ++ verify_results "def" $DF $RF 6 && verify_results "tst" $DF $RF 7 + ret_val=$? + else + ret_val=1 +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +index fc3ee939e..bb40d89be 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +@@ -61,6 +61,16 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -93,6 +103,10 @@ + + + ++ ++ ++ ++ + + + +@@ -133,6 +147,12 @@ + .status.conditions[:] + + ++ ++ /tmp ++ openshift-logging.yaml ++ .status.conditions[:]['nonexistent','dummy'] ++ ++ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +index 8b0614fc6..83910ed38 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +@@ -66,9 +66,7 @@ function test_probes_yamlfilecontent_types { + assert_exists 3 $co'/object[@flag="error"]' + assert_exists 3 $co'/object[@flag="error"]/message' + +- #rm -f $result + rm -f $YAML_FILE +- + } + + test_probes_yamlfilecontent_types + +From 8d64db6203c02d3326c9e3336464bb2333d8f9f4 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Wed, 23 Sep 2020 13:34:59 +0200 +Subject: [PATCH 5/8] Revert changes in base OVAL schema + +And adjust probe implementation and tests for the workaround (lowercase +and escape capital letters in field names with ^, use # instead +of empty field names for scalars). +--- + .../5.11.3/independent-definitions-schema.xsd | 8 ++-- + ...ependent-system-characteristics-schema.xsd | 4 +- + .../oval/5.11.3/oval-definitions-schema.xsd | 14 +++++- + .../oval-system-characteristics-schema.xsd | 7 ++- + .../independent/yamlfilecontent_probe.c | 43 ++++++++++++++++--- + .../test_probes_yamlfilecontent_array.xml | 4 +- + .../test_probes_yamlfilecontent_key.xml | 23 ++++++++-- + ...st_probes_yamlfilecontent_offline_mode.xml | 6 +-- + .../test_probes_yamlfilecontent_types.sh | 38 ++++++++-------- + 9 files changed, 106 insertions(+), 41 deletions(-) + +diff --git a/schemas/oval/5.11.3/independent-definitions-schema.xsd b/schemas/oval/5.11.3/independent-definitions-schema.xsd +index 0f202abf6..4168b45bf 100644 +--- a/schemas/oval/5.11.3/independent-definitions-schema.xsd ++++ b/schemas/oval/5.11.3/independent-definitions-schema.xsd +@@ -2115,7 +2115,7 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a sequence or a map (part of a map) of scalar values which will be accessible in OVAL via instances of the value entity. Any results from evaluating the YAML Path expression other than a sequence (or a map) of scalar values (e.g. sequence of sequences, sequence of maps, map of maps etc.) are considered as incorrect, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. + + + +@@ -2158,12 +2158,12 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. Note that "equals" is the only valid operator for the yamlpath entity. + + + + +- The value entity specifies how to test objects in the value set of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values, set name attribute of the field child element to an empty string (''). The check is entirely controlled by operator attributes of the field element. ++ The value entity specifies how to test objects in the value set of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values (where there is no key to associate), set the name attribute of the field element to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they will be converted to the lowercase and escaped using the '^' symbol (the '^' symbol would be escaped as well). For example 'myCamelCase^Key' would be collected as 'my^camel^case^^^key'. The check is entirely controlled by operator attributes of the field element. + + + +@@ -2172,7 +2172,7 @@ + + + +- ++ + + + +diff --git a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +index b2934d4c6..9c108297b 100644 +--- a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd ++++ b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +@@ -606,12 +606,12 @@ + + + +- Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. This YAML Path expression must evaluate to a list or a map (part of a map) of zero or more scalar values which will be accessible in OVAL via instances of the result entity. Any results from evaluating the YAML Path expression other than a list of scalar values (or a map with scalar values) at the bottom level (e.g. list of lists, list of maps, map with maps or lists) is considered an error, so the author should define the YAML Path expression carefully. Note that "equals" is the only valid operator for the yamlpath entity. ++ Specifies an YAML Path expression to evaluate against the YAML file specified by the filename entity. Note that "equals" is the only valid operator for the yamlpath entity. + + + + +- The value entity holds the target(s) of the specified YAML Path. If YAML Path target is a signle scalar value or a list of scalar values, the field child element's name would be empty (''). The check is entirely controlled by operator attributes of the field element. ++ The value entity holds the target(s) of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values (where there is no key to associate), set the name attribute of the field element to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they will be converted to the lowercase and escaped using the '^' symbol (the '^' symbol would be escaped as well). For example 'myCamelCase^Key' would be collected as 'my^camel^case^^^key'. The check is entirely controlled by operator attributes of the field element. + + + +diff --git a/schemas/oval/5.11.3/oval-definitions-schema.xsd b/schemas/oval/5.11.3/oval-definitions-schema.xsd +index a57b889cf..f43abd318 100644 +--- a/schemas/oval/5.11.3/oval-definitions-schema.xsd ++++ b/schemas/oval/5.11.3/oval-definitions-schema.xsd +@@ -1553,8 +1553,13 @@ + + + +- A string. ++ A string restricted to disallow upper case characters. + ++ ++ ++ ++ ++ + + + +@@ -1819,8 +1824,13 @@ + + + +- A string. ++ A string restricted to disallow upper case characters. + ++ ++ ++ ++ ++ + + + +diff --git a/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd b/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd +index 543b2db71..30962f330 100644 +--- a/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd ++++ b/schemas/oval/5.11.3/oval-system-characteristics-schema.xsd +@@ -574,8 +574,13 @@ + + + +- A string. ++ A string restricted to disallow upper case characters. + ++ ++ ++ ++ ++ + + + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 8e276dc3c..641709bb9 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -148,6 +148,39 @@ do { \ + ret = -1; \ + } while (0) + ++static char *escape_key(char *key) ++{ ++ if (key == NULL) ++ return NULL; ++ ++ size_t cap_letters = 0; ++ size_t key_len = strlen(key); ++ for (size_t i = 0; i < key_len; i++) ++ if ((key[i] >= 'A' && key[i] <= 'Z') || key[i] == '^') ++ cap_letters++; ++ ++ if (cap_letters == 0) ++ return key; ++ ++ char *new_key = realloc(key, key_len + 1 + cap_letters); ++ if (new_key == NULL) ++ return key; ++ new_key[key_len + cap_letters] = '\0'; ++ ++ for (ssize_t i = key_len; i >= 0; i--) { ++ if ((new_key[i] >= 'A' && new_key[i] <= 'Z') || new_key[i] == '^') { ++ if (new_key[i] != '^') ++ new_key[i] += 32; ++ memmove(new_key + i + cap_letters, new_key + i, key_len - i); ++ new_key[i + cap_letters - 1] = '^'; ++ cap_letters--; ++ key_len = i; ++ } ++ } ++ ++ return new_key; ++} ++ + static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, struct oscap_list *values, probe_ctx *ctx) + { + int ret = 0; +@@ -174,7 +207,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + bool sequence = false; + bool mapping = false; + int index = 0; +- char *key = strdup(""); ++ char *key = strdup("#"); + + struct oscap_htable *record = NULL; + +@@ -194,7 +227,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + if (event_type == YAML_SEQUENCE_END_EVENT) { + sequence = false; + } else if (event_type == YAML_SEQUENCE_START_EVENT) { +- result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); ++ result_error("YAML path '%s' points to a multi-dimensional structure (sequence containing another sequence)", yaml_path_cstr); + goto cleanup; + } + } else { +@@ -214,13 +247,13 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + } + record = NULL; + } else if (event_type == YAML_MAPPING_START_EVENT) { +- result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); ++ result_error("YAML path '%s' points to a multi-dimensional structure (map containing another map)", yaml_path_cstr); + goto cleanup; + } + } else { + if (event_type == YAML_MAPPING_START_EVENT) { + if (record) { +- result_error("YAML path '%s' points to a multi-dimensional structure", yaml_path_cstr); ++ result_error("YAML path '%s' points to an invalid structure (map containing another map)", yaml_path_cstr); + goto cleanup; + } + mapping = true; +@@ -235,7 +268,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + if (!sequence) { + if (index++ % 2 == 0) { + free(key); +- key = strdup((const char *) event.data.scalar.value); ++ key = escape_key(strdup((const char *) event.data.scalar.value)); + goto next; + } + } +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +index 9a4227fb8..9fd92492e 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +@@ -77,13 +77,13 @@ + + + +- secureforward-offcluster ++ secureforward-offcluster + + + + + +- ++ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +index bb40d89be..05757d0c8 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +@@ -71,6 +71,15 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -107,6 +116,9 @@ + + + ++ ++ ++ + + + +@@ -153,19 +165,24 @@ + .status.conditions[:]['nonexistent','dummy'] + + ++ ++ /tmp ++ openshift-logging.yaml ++ .spec.outputs ++ + + + + + + +- LogForwarding ++ LogForwarding + + + + + +- openshift-logging ++ openshift-logging + + + +@@ -181,7 +198,7 @@ + True + AsExpected + Upgradeable +- ^\d+-\d+-.*Z$ ++ ^\d+-\d+-.*Z$ + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml +index 5c7cf5b28..35a646688 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_offline_mode.xml +@@ -86,19 +86,19 @@ + + + +- instance ++ instance + + + + + +- outstance ++ outstance + + + + + +- instance ++ instance + + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +index 83910ed38..4f110f6eb 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_types.sh +@@ -24,42 +24,42 @@ function test_probes_yamlfilecontent_types { + + sd='/oval_results/results/system/oval_system_characteristics/system_data' + +- assert_exists 8 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean"]' +- assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean" and text()="true"]' +- assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="boolean" and text()="false"]' ++ assert_exists 8 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="boolean"]' ++ assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="boolean" and text()="true"]' ++ assert_exists 4 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="boolean" and text()="false"]' + +- assert_exists 5 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int"]' ++ assert_exists 5 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int"]' + # int_10: 42 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="42"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int" and text()="42"]' + # int_10_neg: -17 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="-17"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int" and text()="-17"]' + # int_8: 0o33 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="27"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int" and text()="27"]' + # int_16: 0xFF +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="255"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int" and text()="255"]' + # int_cast: !!int "369" +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="int" and text()="369"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="int" and text()="369"]' + +- assert_exists 7 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float"]' ++ assert_exists 7 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float"]' + # float: 7.4 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="7.400000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="7.400000"]' + # float_neg: -0.3 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="-0.300000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="-0.300000"]' + # float_exp: +12e03 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="12000.000000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="12000.000000"]' + # float_exp_neg: -43e-4 +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="-0.004300"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="-0.004300"]' + # float: .inf +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="inf"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="inf"]' + # float: .NAN +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="nan"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="nan"]' + # float_cast: !!float "978.65" +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype="float" and text()="978.650000"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype="float" and text()="978.650000"]' + + # string_true +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype!="boolean" and text()="true"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype!="boolean" and text()="true"]' + # string_number +- assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="" and @datatype!="int" and text()="81"]' ++ assert_exists 1 $sd'/ind-sys:yamlfilecontent_item/ind-sys:value/field[@name="#" and @datatype!="int" and text()="81"]' + + # bool_error_cast, int_error_cast, float_error_cast + co='/oval_results/results/system/oval_system_characteristics/collected_objects' + +From 5f9b9b6686e9d1b05094cdda8d5d1339ed4e8547 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Thu, 24 Sep 2020 11:21:02 +0200 +Subject: [PATCH 6/8] Bump yaml-filter + +--- + src/OVAL/probes/independent/yamlfilecontent_probe.c | 3 +-- + .../yamlfilecontent/test_probes_yamlfilecontent_array.xml | 2 +- + yaml-filter | 2 +- + 3 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 641709bb9..b57956765 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -216,8 +216,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + result_error("YAML parser error: %s", parser.problem); + goto cleanup; + } +- if (!yaml_path_filter_event(yaml_path, &parser, &event, +- YAML_PATH_FILTER_RETURN_ALL)) { ++ if (yaml_path_filter_event(yaml_path, &parser, &event) == YAML_PATH_FILTER_RESULT_OUT) { + goto next; + } + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +index 9fd92492e..c05c5fbb9 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +@@ -62,7 +62,7 @@ + + /tmp + openshift-logging.yaml +- .spec.outputs[0:2].name ++ .spec.outputs[:].name + + + + +From 183e3517365890d0e704460fdb6e99672c469d22 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Tue, 29 Sep 2020 10:42:22 +0200 +Subject: [PATCH 7/8] Update schemas + +--- + .../5.11.3/independent-definitions-schema.xsd | 2 +- + ...dependent-system-characteristics-schema.xsd | 2 +- + .../probes/independent/yamlfilecontent_probe.c | 18 +++++++++--------- + 3 files changed, 11 insertions(+), 11 deletions(-) + +diff --git a/schemas/oval/5.11.3/independent-definitions-schema.xsd b/schemas/oval/5.11.3/independent-definitions-schema.xsd +index 4168b45bf..d9a891d23 100644 +--- a/schemas/oval/5.11.3/independent-definitions-schema.xsd ++++ b/schemas/oval/5.11.3/independent-definitions-schema.xsd +@@ -2163,7 +2163,7 @@ + + + +- The value entity specifies how to test objects in the value set of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values (where there is no key to associate), set the name attribute of the field element to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they will be converted to the lowercase and escaped using the '^' symbol (the '^' symbol would be escaped as well). For example 'myCamelCase^Key' would be collected as 'my^camel^case^^^key'. The check is entirely controlled by operator attributes of the field element. ++ The value entity specifies how to test objects in the value set of the specified YAML Path. To define tests for a single scalar value or a list of scalar values (where there is no key to associate), set the name attribute of the field element to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they should be converted to the lowercase and escaped using the '^' symbol (the '^' symbol should be escaped as well). For example, to check a value associated with 'myCamelCase^Key' set the name attribute of the field to 'my^camel^case^^^key'. The check is entirely controlled by operator attributes of the field element. + + + +diff --git a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +index 9c108297b..c2a2aab50 100644 +--- a/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd ++++ b/schemas/oval/5.11.3/independent-system-characteristics-schema.xsd +@@ -611,7 +611,7 @@ + + + +- The value entity holds the target(s) of the specified YAML Path. To define tests for a signle scalar value or a list of scalar values (where there is no key to associate), set the name attribute of the field element to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they will be converted to the lowercase and escaped using the '^' symbol (the '^' symbol would be escaped as well). For example 'myCamelCase^Key' would be collected as 'my^camel^case^^^key'. The check is entirely controlled by operator attributes of the field element. ++ The value entity holds the target(s) of the specified YAML Path. A single scalar value or a list of scalar values (where there is no key to associate) would have the name attribute of the field element set to '#'. Due to the limitation of the record type field names could not contain uppercase letters, they will be converted to the lowercase and escaped using the '^' symbol (the '^' symbol would be escaped as well). For example 'myCamelCase^Key' would be collected as 'my^camel^case^^^key'. + + + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index b57956765..72e5638ae 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -139,15 +139,6 @@ static SEXP_t *yaml_scalar_event_to_sexp(yaml_event_t *event) + return SEXP_string_new(value, strlen(value)); + } + +-#define result_error(fmt, args...) \ +-do { \ +- SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, fmt, args); \ +- probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); \ +- SEXP_free(msg); \ +- probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); \ +- ret = -1; \ +-} while (0) +- + static char *escape_key(char *key) + { + if (key == NULL) +@@ -181,6 +172,15 @@ static char *escape_key(char *key) + return new_key; + } + ++#define result_error(fmt, args...) \ ++do { \ ++ SEXP_t *msg = probe_msg_creatf(OVAL_MESSAGE_LEVEL_ERROR, fmt, args); \ ++ probe_cobj_add_msg(probe_ctx_getresult(ctx), msg); \ ++ SEXP_free(msg); \ ++ probe_cobj_set_flag(probe_ctx_getresult(ctx), SYSCHAR_FLAG_ERROR); \ ++ ret = -1; \ ++} while (0) ++ + static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, struct oscap_list *values, probe_ctx *ctx) + { + int ret = 0; + +From 227cce0560574d3f2efd75e884152f1fb1a7c619 Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Tue, 29 Sep 2020 15:31:41 +0200 +Subject: [PATCH 8/8] probes/yamlfilecontent: Add missed SEXP_free + +--- + src/OVAL/probes/independent/yamlfilecontent_probe.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 72e5638ae..6f18abf83 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -359,6 +359,7 @@ static int process_yaml_file(const char *prefix, const char *path, const char *f + } + oscap_htable_iterator_free(record_it); + SEXP_list_add(item, result_ent); ++ SEXP_free(result_ent); + } + probe_item_collect(ctx, item); + } diff --git a/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-submodule-PR_1552.patch b/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-submodule-PR_1552.patch new file mode 100644 index 0000000..7712c0b --- /dev/null +++ b/SOURCES/openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-submodule-PR_1552.patch @@ -0,0 +1,2641 @@ +diff --git a/.travis.yml b/.travis.yml +index 7e776c3..7382736 100644 +--- a/.travis.yml ++++ b/.travis.yml +@@ -1,15 +1,29 @@ + language: c +-dist: bionic ++ ++os: ++ - linux + + before_script: +- - mkdir -p build +- - cd build +- - sudo apt-get install -y libyaml-0-2 ++ - mkdir -p build ++ - cd build ++ - sudo apt-get install -y libyaml-0-2 + + script: +- - cmake -DENABLE_COVERAGE=yes .. +- - make && ctest -V +- - make gcov ++ - cmake -DENABLE_COVERAGE=yes .. ++ - build-wrapper-linux-x86-64 --out-dir bw-output make clean all || true # Allowed to fail in builds from forks ++ - make && ctest -V ++ - make gcov ++ - sonar-scanner -Dproject.settings=../sonar-project.properties || true # Allowed to fail in builds from forks + + after_success: +- - curl -s https://codecov.io/bash > cov.sh && bash cov.sh -x "$GCOV" ++ - curl -s https://codecov.io/bash > cov.sh && bash cov.sh -x "$GCOV" ++ ++addons: ++ sonarcloud: ++ organization: "openscap" ++ token: ++ secure: "BoPCYdulv5+7sNQtShKE0tYVelHSL3sWNhjz5QFZCDefK7az2+Lze9Zp/sas02gmBLoc0vuuFjFgtKLSPt9mEL4YabwAysCF445jwE+wu8KJf/Bz36tMz1gE9383+Ic9LcR2bdPBY/0gBWZpmKK33mNs+P5xuI23XsK2Whkjev9tg/kjjXAd3Q79WqlDW2SKT5Ugg5SxE6RgFS/pBsxWsqp3Vfx38t3hIhloECf51/aVrBabpYmwYe8l8gq/+A2PO/gpw6SBdCu2Y9x+zlAhWkY7cri7C8LSUSMS0pUM5haij4oO/7UVUbRjWmTDpg3sSLtJka2BIIl32XINMdvBCzbhIpuNCTZlWz3KCyBWRvV8r95n+p2IahbV7ZU/Jc8QvBNC0IsjCjHORtuJzXOa3BCZ2PXggboX1uaHkLe+xECC/3gjLDXAcUvM6QJN2Ytbnzfd2jTlhCt1a3ttCPUvSqN9CIJWIKnbpHPWwAk7YuMH7GXbCle9mInDvPOe16KTQ39RMsWRE1HgyTHErfT5pyaCwv2lq4oThuCrUyAGmMYgC6OZalW9DciQwp/kzKECnrwfjCCy8uJu/BlXWGMeJWjRaLFpOpZLIPaVVvKlODb8zZUZeVV1sqyyQ6+I4Opfmi0ikVW4eOqyXRy1G1O9OxXeHC2SHFuK4E5cks05EA4=" ++ ++cache: ++ directories: ++ - '$HOME/.sonar/cache' +\ No newline at end of file +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 796709d..9c14766 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -1,4 +1,5 @@ + cmake_minimum_required(VERSION 2.8) ++project(yaml-path C) + + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + +@@ -6,35 +7,35 @@ include(FindPkgConfig) + pkg_check_modules(YAML yaml-0.1) + find_package(codecov) + +-include_directories(${YAML_INCLUDE_DIRS}) ++include_directories(${YAML_INCLUDE_DIRS} src) + +-add_library(yaml-path yaml-path.c) ++add_library(yaml-path src/yaml-path.c) + target_link_libraries(yaml-path ${YAML_LIBRARIES}) ++add_coverage(yaml-path) + +-add_executable(ya yaml.c) +-target_link_libraries(ya yaml-path) +-add_coverage(ya) +- +-add_executable(yamlp yamlp.c) ++add_executable(yamlp src/yamlp.c) + target_link_libraries(yamlp yaml-path) + add_coverage(yamlp) + +-add_coverage(yaml-path) +- +-install(TARGETS ya RUNTIME DESTINATION bin) + install(TARGETS yamlp RUNTIME DESTINATION bin) + +-add_executable(test-path-segments test-path-segments.c yaml-path.c) +-add_coverage(test-path-segments) ++if(${CMAKE_C_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_C_COMPILER_ID} STREQUAL "Clang") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pipe -std=c99 -W -Wall -Wnonnull -Wshadow -Wformat -Wundef -Wno-unused-parameter -Wmissing-prototypes -Wno-unknown-pragmas -D_GNU_SOURCE -D_POSIX_C_SOURCE=200112L") ++endif() ++if(${CMAKE_SYSTEM_NAME} EQUAL "Solaris") ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D__EXTENSIONS__") ++endif() ++if(WIN32) ++ # Expose new WinAPI function appearing on Windows 7 (e.g. inet_pton) ++ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_WIN32_WINNT=0x0600") ++endif() ++if(APPLE) ++ # Full Single Unix Standard v3 (SUSv3) conformance (the Unix API) ++ add_definitions(-D_DARWIN_C_SOURCE) ++endif() + +-add_executable(test-paths test-paths.c) +-target_link_libraries(test-paths yaml-path) +-add_coverage(test-paths) +-list(APPEND LCOV_REMOVE_PATTERNS "'${CMAKE_SOURCE_DIR}/test-*'") ++enable_testing() ++add_subdirectory("tests") + + coverage_evaluate() + +-enable_testing() +-add_test(NAME "test-path-segments" COMMAND ${CMAKE_BINARY_DIR}/test-path-segments) +-add_test(NAME "test-paths" COMMAND ${CMAKE_BINARY_DIR}/test-paths) +-add_test(NAME "test-yamlp" COMMAND ${CMAKE_SOURCE_DIR}/test-yamlp.sh) +diff --git a/README.md b/README.md +index 9f66685..26ecd44 100644 +--- a/README.md ++++ b/README.md +@@ -1,3 +1,9 @@ +-# YAML Path filter ++[![Build Status](https://travis-ci.org/OpenSCAP/yaml-filter.svg?branch=master)](https://travis-ci.org/OpenSCAP/yaml-filter) [![Total alerts](https://img.shields.io/lgtm/alerts/g/OpenSCAP/yaml-filter.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/OpenSCAP/yaml-filter/alerts/) + +-YAML document filtering for libyaml ++# YAML filter ++ ++YAML documnets filtering library (based on [libyaml](https://github.com/yaml/libyaml)). ++ ++[How to build and test it](docs/developer.md). ++ ++[YAML Path definition (v1)](docs/yaml_path_v1.md). +\ No newline at end of file +diff --git a/docs/developer.md b/docs/developer.md +new file mode 100644 +index 0000000..1d396af +--- /dev/null ++++ b/docs/developer.md +@@ -0,0 +1,86 @@ ++# Developer's Guide ++ ++## Building on Linux ++ ++If you want to build the `libyaml-path` library and `yamlp` filtering utility follow the these instructions: ++ ++ ++### 1. Get the source code ++ ++```sh ++$ git clone https://github.com/OpenSCAP/yaml-filter.git ++$ cd yaml-filter ++``` ++ ++ ++### 2. *Get the build dependencies* ++ ++To build the library you will also need to install the build dependencies. ++ ++The project relies on CMake (`cmake`) build system and C99 compiler (for example `gcc`). ++ ++The only mandatroy dependency of `libyaml-path` is the YAML document parser/emitter library `libyaml`. You can also use `lcov` for coverage reports, but it is optional. ++ ++```sh ++# Ubuntu ++$ sudo apt-get install -y libyaml-0-2 ++``` ++ ++```sh ++# Fedora ++$ sudo dnf install -y libyaml lcov ++``` ++ ++When you have all the build dependencies installed you can build the library. ++ ++ ++### 3. *Build the project* ++ ++Run the following commands to build the library and filtering utility: ++ ++```sh ++$ mkdir -p build ++$ cd build/ ++$ cmake .. ++$ make ++``` ++ ++ ++### 3. *Run the tests* ++ ++Now you can execute the following command to run library self-checks: ++ ++```sh ++$ ctest ++``` ++ ++ ++### 4. *Install* ++ ++Run the installation procedure by executing the following command: ++ ++```sh ++$ make install ++``` ++ ++You can also configure CMake to install everything into the $HOME/.local directory: ++ ++```sh ++$ cd build ++$ rm -rf * ++$ cmake -DCMAKE_INSTALL_PREFIX=$HOME/.local .. ++$ make ++$ make install ++``` ++ ++ ++### 5. *Generate code coverage report* ++ ++You can use `lcov` for code coverage report generation. It is integrated into the project's build configuration: ++ ++```sh ++$ cd build ++$ cmake -DENABLE_COVERAGE=yes .. ++$ make && ctest -V ++$ make gcov ++``` +\ No newline at end of file +diff --git a/docs/yaml_path_v1.md b/docs/yaml_path_v1.md +new file mode 100644 +index 0000000..e84dbb3 +--- /dev/null ++++ b/docs/yaml_path_v1.md +@@ -0,0 +1,119 @@ ++### v1.0.0 2020-09-28 ++ ++## Overview ++This implementation is a subset of the intersection between [JSONPath](https://goessner.net/articles/JsonPath) and [YAML Path](https://pypi.org/project/yamlpath), and it focuses on addressing elements inside YAML files (node descriptors). There are no *query*-like capabilities. ++ ++Example YAML structure: ++```yaml ++foo: ++ - bar: &bar True ++ first: First Bar ++ second: 2 ++ arr: [1, 2, 3] ++ - baz: False ++ other_bar: *bar ++ first: First Baz ++ some.el/here: Delimiters... ++ "bar's": 0 ++``` ++ ++Example JSON structure: ++```json ++{ ++ "foo": [ ++ { ++ "bar": true, ++ "first": "First Bar", ++ "second": 2, ++ "arr": [1, 2, 3] ++ }, ++ { ++ "baz": false, ++ "other_bar": true, ++ "first": "First Baz", ++ "some.el/here": "Delimiters...", ++ "bar's": 0, ++ } ++ ] ++} ++``` ++ ++## Delimiters ++Dot-notation (`.`) is the only supported notation to define *map key* path segments. Both *map key* and *sequence index* path segments could also be defined using square brackets (`[`, `]`). For *map key* and *map keys selection* segments both single (`'`) and double (`"`) quotes are supported, `['key']` is the same as `["key"]` and `['key',"other's key"]` is a valid path segment. ++ ++For example: `$.foo[0].bar` or `.foo[0]['bar']` or `['foo'][0].bar` or `foo[1]["bar's"]`. ++ ++## Special symbols ++A path might be prefixed by a dollar sign and a dot (`$`, `.`), but this prefix is retained for compatibility with *JSONPath* and not mandatory. Implicit *document root* is assumed unless the path explicitly starts with an *anchor* (`&...`) segment (see below for details). ++ ++If the first segment is a *map key* segment (and explicit *document root* is omitted) the initial dot (`.`) is also not mandatory. ++ ++For example, these paths are equal: `$.el`, `.el`, `el`, `['el']`. And they all address the value stored in the "el" key of the top-most map of the document. ++ ++An asterisk (`*`) as a key name has a special meaning, and treated as an all-inclusive *keys selection* section (see below). That's it, `$.*` expression would include all keys of the map in the document root. The `[*]` syntax is also valid. One should use the `[:]` notation to acheive same effect for sequences (include all indices). ++ ++## Path Segment Types ++ ++#### Document Root ++`$` ++ ++Optional explicit document root. Only allowed to appear at the beginning of the path. ++ ++```python ++$.foo[0].bar = .foo[0].bar = foo[0].bar ++== true ++``` ++ ++ ++#### Map Key ++`.map.key` or `.map['key']` ++ ++```python ++$.foo[0].second = ['foo'][0]['second'] ++== 2 ++``` ++ ++ ++#### Map Keys Selection ++`.map['key1','key2',...'keyN']` ++ ++Special syntax for the all-inclusive key selection: `.*`. Also, there is the `[*]` variant of this syntax. ++ ++```python ++$.foo[0]['first','second'] = ['foo'][0]['first','second'] ++== {"first": "First Bar", "second": 2} ++ ++foo[0]['first','second','bar','arr'] = foo[0].* ++== {"bar": true, "first": "First Bar", "second": 2, "arr": [1, 2, 3]} ++``` ++ ++ ++#### Sequence Index ++`.array[]` ++ ++```python ++$.foo[0] ++== {"bar": True, "first": "First Bar", "second": 2} ++``` ++ ++ ++#### Sequence Indices Set ++`.array[,,...]` ++ ++Special syntax for the all-inclusive indices set: `[:]`. ++ ++```python ++$.foo[0].arr[0,1,2] = foo[0].arr[:] ++== [1, 2, 3] ++``` ++ ++ ++#### Anchor ++`&anchor` ++ ++Matches elements starting from the given anchor instead of the document root. This segment is only sensible in paths for YAML documents as there is no anchors/aliases concept in the JSON specification. ++ ++```python ++$.foo[0].bar = &bar ++== True ++``` +\ No newline at end of file +diff --git a/openshift-logging.yaml b/res/openshift-logging.yaml +similarity index 100% +rename from openshift-logging.yaml +rename to res/openshift-logging.yaml +diff --git a/res/openshift-upgradeable.yaml b/res/openshift-upgradeable.yaml +new file mode 100644 +index 0000000..e962fdd +--- /dev/null ++++ b/res/openshift-upgradeable.yaml +@@ -0,0 +1,39 @@ ++apiVersion: config.openshift.io/v1 ++kind: ClusterOperator ++metadata: ++ annotations: ++ exclude.release.openshift.io/internal-openshift-hosted: "true" ++ creationTimestamp: "2020-06-08T04:36:36Z" ++ generation: 1 ++ name: openshift-apiserver ++ resourceVersion: "53861" ++ selfLink: /apis/config.openshift.io/v1/clusteroperators/openshift-apiserver ++spec: {} ++status: ++ conditions: ++ - lastTransitionTime: "2020-06-08T04:54:58Z" ++ reason: AsExpected ++ status: "False" ++ type: Degraded ++ - lastTransitionTime: "2020-06-08T06:34:00Z" ++ reason: AsExpected ++ status: "False" ++ type: Progressing ++ - lastTransitionTime: "2020-06-08T04:51:08Z" ++ reason: AsExpected ++ status: "True" ++ type: Available ++ - lastTransitionTime: "2020-06-08T04:45:45Z" ++ reason: AsExpected ++ status: "True" ++ type: Upgradeable ++ extension: null ++ relatedObjects: ++ - group: operator.openshift.io ++ name: cluster ++ resource: openshiftapiservers ++ versions: ++ - name: operator ++ version: 4.5.0-0.nightly-2020-06-04-214605 ++ - name: openshift-apiserver ++ version: 4.5.0-0.nightly-2020-06-04-214605 +\ No newline at end of file +diff --git a/sonar-project.properties b/sonar-project.properties +new file mode 100644 +index 0000000..8983591 +--- /dev/null ++++ b/sonar-project.properties +@@ -0,0 +1,8 @@ ++sonar.organization=openscap ++ ++sonar.projectKey=OpenSCAP_yaml-filter ++sonar.projectName=yaml-filter ++ ++sonar.sources=. ++ ++sonar.cfamily.build-wrapper-output=bw-output +diff --git a/src/yaml-path.c b/src/yaml-path.c +new file mode 100644 +index 0000000..004c61d +--- /dev/null ++++ b/src/yaml-path.c +@@ -0,0 +1,829 @@ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "yaml-path.h" ++ ++ ++#define YAML_PATH_MAX_SECTION_ITEMS 256 ++ ++#define _STR(x) #x ++#define STR(x) _STR(x) ++ ++ ++typedef enum yaml_path_section_type { ++ YAML_PATH_SECTION_ROOT, ++ YAML_PATH_SECTION_ANCHOR, ++ YAML_PATH_SECTION_INDEX, ++ YAML_PATH_SECTION_SET, ++ YAML_PATH_SECTION_KEY, ++ YAML_PATH_SECTION_SELECTION, ++} yaml_path_section_type_t; ++ ++typedef struct yaml_path_selection_key_raw { ++ const char *start; ++ size_t len; ++} yaml_path_selection_key_raw_t; ++ ++ ++typedef struct yaml_path_key { ++ const char *key; ++ TAILQ_ENTRY(yaml_path_key) entries; ++} yaml_path_key_t; ++ ++typedef TAILQ_HEAD(path_key_list, yaml_path_key) path_key_list_t; ++ ++ ++typedef struct yaml_path_section { ++ yaml_path_section_type_t type; ++ size_t level; ++ union { ++ const char *anchor; ++ size_t index; ++ size_t *set; ++ const char *key; ++ path_key_list_t selection; ++ } data; ++ TAILQ_ENTRY(yaml_path_section) entries; ++ ++ yaml_node_type_t node_type; ++ size_t counter; ++ bool valid; ++ bool next_valid; ++} yaml_path_section_t; ++ ++typedef TAILQ_HEAD(path_section_list, yaml_path_section) path_section_list_t; ++ ++ ++struct yaml_path { ++ path_section_list_t sections_list; ++ size_t sections_count; ++ size_t current_level; ++ size_t start_level; ++ ++ yaml_path_error_t error; ++}; ++ ++ ++static size_t ++yaml_path_set_snprint (const size_t *set, char *s, size_t max_len) ++{ ++ assert(set != NULL); ++ if (s == NULL) ++ return -1; ++ size_t len = 0; ++ if (set[0] == 0) { ++ len += snprintf(s, max_len, "[:]"); ++ } else { ++ for (size_t i = 1; i <= set[0]; i++) ++ len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "%s%zu", (len ? "," : "["), set[i]); ++ len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "]"); ++ } ++ return len; ++} ++ ++static size_t ++yaml_path_selection_snprint (const path_key_list_t *selection, char *s, size_t max_len) ++{ ++ assert(selection != NULL); ++ if (s == NULL) ++ return -1; ++ size_t len = 0; ++ yaml_path_key_t *el; ++ if TAILQ_EMPTY(selection) { ++ len += snprintf(s, max_len, ".*"); ++ } else { ++ TAILQ_FOREACH(el, selection, entries) { ++ char quote = strchr(el->key, '\'') ? '"' : '\''; ++ len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "%s%c%s%c", (len ? "," : "["), quote, el->key, quote); ++ } ++ len += snprintf(s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len), "]"); ++ } ++ return len; ++} ++ ++static bool ++yaml_path_set_is_empty (const size_t *set) ++{ ++ assert(set != NULL); ++ return set[0] == 0; ++} ++ ++static bool ++yaml_path_set_has_index (const size_t *set, size_t idx) ++{ ++ assert(set != NULL); ++ for (size_t i = 1; i <= set[0]; i++) ++ if (set[i] == idx) ++ return true; ++ return false; ++} ++ ++static bool ++yaml_path_selection_is_empty (path_key_list_t *selection) ++{ ++ assert(selection != NULL); ++ return TAILQ_EMPTY(selection) ? true : false; ++} ++ ++static const char* ++yaml_path_selection_key_get (path_key_list_t *selection, const char *key) ++{ ++ assert(selection != NULL); ++ yaml_path_key_t *el; ++ TAILQ_FOREACH(el, selection, entries) { ++ if (!strcmp(el->key, key)) ++ return el->key; ++ } ++ return NULL; ++} ++ ++static size_t ++yaml_path_selection_keys_add (path_key_list_t *selection, yaml_path_selection_key_raw_t *raw_keys, size_t count) ++{ ++ assert(selection != NULL); ++ assert(raw_keys != NULL); ++ for (size_t i = 0; i < count; i++) { ++ yaml_path_key_t *el = malloc(sizeof(*el)); ++ if (el == NULL) ++ return i; ++ TAILQ_INSERT_TAIL(selection, el, entries); ++ el->key = strndup(raw_keys[i].start, raw_keys[i].len); ++ if (el->key == NULL) ++ return i; ++ } ++ return count; ++} ++ ++static void ++yaml_path_selection_keys_remove (path_key_list_t *selection) ++{ ++ assert(selection != NULL); ++ while (!TAILQ_EMPTY(selection)) { ++ yaml_path_key_t *el = TAILQ_FIRST(selection); ++ TAILQ_REMOVE(selection, el, entries); ++ free((void *)el->key); ++ free(el); ++ } ++} ++ ++static void ++yaml_path_sections_remove (yaml_path_t *path) ++{ ++ assert(path != NULL); ++ while (!TAILQ_EMPTY(&path->sections_list)) { ++ yaml_path_section_t *el = TAILQ_FIRST(&path->sections_list); ++ TAILQ_REMOVE(&path->sections_list, el, entries); ++ path->sections_count--; ++ switch (el->type) { ++ case YAML_PATH_SECTION_KEY: ++ free((void *)el->data.key); ++ break; ++ case YAML_PATH_SECTION_ANCHOR: ++ free((void *)el->data.anchor); ++ break; ++ case YAML_PATH_SECTION_SET: ++ free((void *)el->data.set); ++ break; ++ case YAML_PATH_SECTION_SELECTION: ++ yaml_path_selection_keys_remove(&el->data.selection); ++ break; ++ default: ++ break; ++ } ++ free(el); ++ } ++} ++ ++static yaml_path_section_t* ++yaml_path_section_create (yaml_path_t *path, yaml_path_section_type_t section_type) ++{ ++ yaml_path_section_t *el = malloc(sizeof(*el)); ++ if (el != NULL) { ++ memset(el, 0, sizeof(*el)); ++ path->sections_count++; ++ el->level = path->sections_count; ++ el->type = section_type; ++ el->node_type = YAML_NO_NODE; ++ TAILQ_INSERT_TAIL(&path->sections_list, el, entries); ++ if (el->type == YAML_PATH_SECTION_SELECTION) { ++ TAILQ_INIT(&el->data.selection); ++ } ++ } ++ return el; ++} ++ ++static size_t ++yaml_path_section_snprint (yaml_path_section_t *section, char *s, size_t max_len) ++{ ++ assert(section != NULL); ++ if (s == NULL) ++ return -1; ++ size_t len; ++ switch (section->type) { ++ case YAML_PATH_SECTION_ROOT: ++ len = snprintf(s, max_len, "$"); ++ break; ++ case YAML_PATH_SECTION_KEY: { ++ char quote = '\0'; ++ if (strpbrk(section->data.key, "[]().$&*")) ++ quote = strchr(section->data.key, '\'') ? '"' : '\''; ++ if (quote) { ++ len = snprintf(s, max_len, "[%c%s%c]", quote, section->data.key, quote); ++ } else { ++ len = snprintf(s, max_len, ".%s", section->data.key); ++ } ++ } ++ break; ++ case YAML_PATH_SECTION_ANCHOR: ++ len = snprintf(s, max_len, "&%s", section->data.anchor); ++ break; ++ case YAML_PATH_SECTION_INDEX: ++ len = snprintf(s, max_len, "[%zu]", section->data.index); ++ break; ++ case YAML_PATH_SECTION_SET: ++ len = yaml_path_set_snprint(section->data.set, s, max_len); ++ break; ++ case YAML_PATH_SECTION_SELECTION: ++ len = yaml_path_selection_snprint(§ion->data.selection, s, max_len); ++ break; ++ default: ++ len = snprintf(s, max_len, ""); ++ break; ++ } ++ return len; ++} ++ ++static void ++yaml_path_error_set (yaml_path_t *path, yaml_path_error_type_t error_type, const char *message, size_t pos) ++{ ++ assert(path != NULL); ++ path->error.type = error_type; ++ path->error.message = message; ++ path->error.pos = pos; ++} ++ ++static void ++yaml_path_error_clear (yaml_path_t *path) ++{ ++ yaml_path_error_set(path, YAML_PATH_ERROR_NONE, NULL, 0); ++} ++ ++#define return_with_error(error_type, error_text, error_pos) \ ++do { \ ++ yaml_path_error_set(path, error_type, (error_text), (error_pos)); \ ++ goto error; \ ++} while (0) ++ ++static void ++yaml_path_parse_impl (yaml_path_t *path, char *s_path) { ++ char *sp = s_path; ++ char *spe = NULL; ++ ++ assert(path != NULL); ++ ++ if (s_path == NULL || !s_path[0]) { ++ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Path string is NULL or empty", 0); ++ return; ++ } ++ ++ while (*sp != '\0') { ++ switch (*sp) { ++ case '.': ++ case '[': ++ if (path->sections_count == 0) { ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ } ++ if (*sp == '.') { ++ // Key or Selection ++ spe = sp + 1; ++ if (*spe == '*') { ++ // Empty key selection section means that all keys were selected ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ while (*spe != '.' && *spe != '[' && *spe != '\0') ++ spe++; ++ } else { ++ while (*spe != '.' && *spe != '[' && *spe != '\0') ++ spe++; ++ if (spe == sp+1) ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path); ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.key = strndup(sp + 1, spe-sp - 1); ++ if (sec->data.key == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path); ++ } ++ sp = spe-1; ++ } else if (*sp == '[') { ++ spe = sp + 1; ++ if (*spe == '*' && *(spe+1) == ']') { ++ // Empty key selection section means that all keys were selected ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sp = spe+1; ++ } else if (*spe == '\'' || *spe == '"') { ++ // Key(s) ++ size_t keys_count = 0; ++ yaml_path_selection_key_raw_t raw_keys[YAML_PATH_MAX_SECTION_ITEMS] = {{NULL, 0}}; ++ sp = spe; ++ while (*spe != ']' && *spe != '\0') { ++ if (keys_count >= YAML_PATH_MAX_SECTION_ITEMS) ++ return_with_error(YAML_PATH_ERROR_SECTION, "Segment keys selection has reached the limit of keys: "STR(YAML_PATH_MAX_SECTION_ITEMS), sp - s_path); ++ char quote = *spe; ++ spe++; ++ while (*spe != quote && *spe != '\0') ++ spe++; ++ if (*spe == '\0') ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (unexpected end of string, missing closing quotation mark)", sp - s_path); ++ if (spe == sp+1) ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path); ++ spe++; ++ if (*spe == '\0') ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (unexpected end of string, missing ']')", sp - s_path); ++ if (*spe == ']') { ++ raw_keys[keys_count].start = sp + 1; ++ raw_keys[keys_count].len = spe-sp - 2; ++ keys_count++; ++ } else if (*spe == ',') { ++ spe++; ++ if (*spe != '\'' && *spe != '"') ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment keys selection is invalid (invalid character)", spe - s_path); ++ raw_keys[keys_count].start = sp + 1; ++ raw_keys[keys_count].len = spe-sp - 3; ++ keys_count++; ++ sp = spe; ++ } else { ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment key is invalid (invalid character)", spe - s_path); ++ } ++ } ++ if (keys_count == 1) { ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.key = strndup(raw_keys[0].start, raw_keys[0].len); ++ if (sec->data.key == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path); ++ } else if (keys_count > 1) { ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SELECTION); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ if (yaml_path_selection_keys_add(&sec->data.selection, raw_keys, keys_count) != keys_count) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (keys selection)", sp - s_path); ++ } ++ sp = spe; ++ } else { ++ // Indices ++ if (*spe == ':' && *(spe+1) == ']') { ++ // Set (all-inclusive) ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SET); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.set = malloc(sizeof(size_t) * 1); ++ if (sec->data.set == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (set)", sp - s_path); ++ sec->data.set[0] = 0; ++ sp = spe+1; ++ } else { ++ while (*spe == ' ' || *spe == '\t') ++ spe++; ++ if (*spe == '-') ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (negative number)", spe - s_path); ++ size_t idx = strtoul(spe, &spe, 10); ++ if (*spe == ']') { ++ // Index ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_INDEX); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.index = idx; ++ sp = spe; ++ } else if (*spe == ',') { ++ // Set ++ size_t indices[YAML_PATH_MAX_SECTION_ITEMS+1] = {0}; ++ while (*spe == ',' && spe > sp+1) { ++ if (indices[0] >= YAML_PATH_MAX_SECTION_ITEMS) ++ return_with_error(YAML_PATH_ERROR_SECTION, "Segment indices set has reached the limit of indices: "STR(YAML_PATH_MAX_SECTION_ITEMS), sp - s_path); ++ sp = spe++; ++ indices[0]++; ++ indices[indices[0]] = idx; ++ while (*spe == ' ' || *spe == '\t') ++ spe++; ++ if (*spe == '-') ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment set index is invalid (negative number)", spe - s_path); ++ idx = strtoul(spe, &spe, 10); ++ } ++ if (*spe == ']' && spe > sp+1) { ++ indices[0]++; ++ indices[indices[0]] = idx; ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SET); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.set = malloc(sizeof(*indices) * (indices[0] + 1)); ++ if (sec->data.set == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (set)", sp - s_path); ++ memcpy(sec->data.set, indices, sizeof(*indices) * (indices[0] + 1)); ++ sp = spe; ++ } else { ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment set is invalid (invalid character)", spe - s_path); ++ } ++ } else if (*spe == '\0') { ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (unexpected end of string, missing ']')", spe - s_path); ++ } else { ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment index is invalid (invalid character)", spe - s_path); ++ } ++ } ++ } ++ } ++ break; ++ case '&': ++ if (path->sections_count == 0) { ++ spe = sp + 1; ++ while (*spe != '.' && *spe != '[' && *spe != '\0') ++ spe++; ++ if (spe - sp > 1) { ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ANCHOR); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.anchor = strndup(sp+1, spe-sp-1); ++ if (sec->data.anchor == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (anchor)", sp - s_path); ++ } else { ++ return_with_error(YAML_PATH_ERROR_PARSE, "Segment anchor is invalid (empty)", spe - s_path); ++ } ++ } else { ++ return_with_error(YAML_PATH_ERROR_SECTION, "Anchor segment is only allowed at the beginning of the path", sp - s_path); ++ } ++ sp = spe - 1; ++ break; ++ case '$': ++ if (path->sections_count == 0) { ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ } else { ++ return_with_error(YAML_PATH_ERROR_SECTION, "Root segment is only allowed at the beginning of the path", sp - s_path); ++ } ++ break; ++ default: ++ if (path->sections_count == 0) { ++ spe = sp + 1; ++ // Special beginning of the path (implicit key) ++ while (*spe != '.' && *spe != '[' && *spe != '\0') ++ spe++; ++ yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); ++ if (sec == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (section)", sp - s_path); ++ sec->data.key = strndup(sp, spe-sp); ++ if (sec->data.key == NULL) ++ return_with_error(YAML_PATH_ERROR_NOMEM, "Unable to allocate memory (key)", sp - s_path); ++ sp = spe-1; ++ } ++ break; ++ } ++ sp++; ++ } ++ ++ if (path->sections_count == 0) ++ return_with_error(YAML_PATH_ERROR_SECTION, "Invalid, empty or meaningless path", 0); ++ ++ return; // OK ++ ++error: ++ yaml_path_sections_remove(path); ++ if (path->error.type == YAML_PATH_ERROR_NONE) ++ yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Unable to parse the path string", 0); ++} ++ ++static yaml_path_section_t* ++yaml_path_section_get_at_level (yaml_path_t *path, size_t level) ++{ ++ assert(path != NULL); ++ yaml_path_section_t *el; ++ TAILQ_FOREACH(el, &path->sections_list, entries) { ++ if (el->level == level) ++ return el; ++ } ++ return NULL; ++} ++ ++static yaml_path_section_t* ++yaml_path_section_get_first (yaml_path_t *path) ++{ ++ return yaml_path_section_get_at_level(path, 1); ++} ++ ++static yaml_path_section_t* ++yaml_path_section_get_current (yaml_path_t *path) ++{ ++ assert(path != NULL); ++ if (!path->start_level) ++ return NULL; ++ return yaml_path_section_get_at_level(path, path->current_level - path->start_level + 1); ++} ++ ++static bool ++yaml_path_section_current_is_last (yaml_path_t *path) ++{ ++ assert(path != NULL); ++ yaml_path_section_t *sec = yaml_path_section_get_current(path); ++ if (sec == NULL) ++ return false; ++ return sec->level == path->sections_count; ++} ++ ++static bool ++yaml_path_sections_prev_are_valid (yaml_path_t *path) ++{ ++ assert(path != NULL); ++ int valid = true; ++ yaml_path_section_t *el; ++ TAILQ_FOREACH(el, &path->sections_list, entries) { ++ if (el->level < path->current_level - path->start_level + 1) ++ valid = el->valid && valid; ++ } ++ return valid; ++} ++ ++static bool ++yaml_path_section_is_mandatory_container (yaml_path_section_t *sec) ++{ ++ assert(sec != NULL); ++ bool res = false; ++ if ((sec->type == YAML_PATH_SECTION_SELECTION && sec->node_type == YAML_MAPPING_NODE) ++ || ++ (sec->type == YAML_PATH_SECTION_SET && sec->node_type == YAML_SEQUENCE_NODE)) ++ res = true; ++ return res; ++} ++ ++static const char * ++yaml_path_filter_event_get_anchor (const yaml_event_t *event) ++{ ++ assert(event != NULL); ++ switch(event->type) { ++ case YAML_MAPPING_START_EVENT: ++ return (const char *)event->data.mapping_start.anchor; ++ case YAML_SEQUENCE_START_EVENT: ++ return (const char *)event->data.sequence_start.anchor; ++ case YAML_SCALAR_EVENT: ++ return (const char *)event->data.scalar.anchor; ++ default: ++ break; ++ } ++ return NULL; ++} ++ ++static bool ++yaml_path_is_valid (yaml_path_t *path) ++{ ++ assert(path != NULL); ++ bool valid = true; ++ yaml_path_section_t *el; ++ TAILQ_FOREACH(el, &path->sections_list, entries) { ++ valid = el->valid && valid; ++ } ++ return valid; ++} ++ ++ ++/* Public API -------------------------------------------------------------- */ ++ ++yaml_path_t* ++yaml_path_create (void) ++{ ++ yaml_path_t *ypath = malloc(sizeof(*ypath)); ++ if (ypath != NULL) { ++ memset (ypath, 0, sizeof(*ypath)); ++ TAILQ_INIT(&ypath->sections_list); ++ } ++ return ypath; ++} ++ ++int ++yaml_path_parse (yaml_path_t *path, char *s_path) ++{ ++ if (path == NULL) ++ return -1; ++ ++ yaml_path_sections_remove(path); ++ yaml_path_error_clear(path); ++ ++ yaml_path_parse_impl(path, s_path); ++ if (path->error.type != YAML_PATH_ERROR_NONE) ++ return -2; ++ ++ return 0; ++} ++ ++void ++yaml_path_destroy (yaml_path_t *path) ++{ ++ if (path == NULL) ++ return; ++ yaml_path_sections_remove(path); ++ free(path); ++} ++ ++const yaml_path_error_t* ++yaml_path_error_get (yaml_path_t *path) ++{ ++ if (path == NULL) ++ return NULL; ++ return &path->error; ++} ++ ++size_t ++yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len) ++{ ++ if (s == NULL) ++ return -1; ++ if (path == NULL) ++ return 0; ++ ++ size_t len = 0; ++ yaml_path_section_t *el; ++ TAILQ_FOREACH(el, &path->sections_list, entries) { ++ len += yaml_path_section_snprint(el, s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len)); ++ } ++ return len; ++} ++ ++yaml_path_filter_result_t ++yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event) ++{ ++ if (path == NULL || parser == NULL || event == NULL || path->sections_count == 0) ++ return YAML_PATH_FILTER_RESULT_OUT; ++ ++ int res = YAML_PATH_FILTER_RESULT_OUT; ++ ++ const char *anchor = yaml_path_filter_event_get_anchor(event); ++ ++ if (!path->start_level) { ++ switch (yaml_path_section_get_first(path)->type) { ++ case YAML_PATH_SECTION_ROOT: ++ if (event->type == YAML_DOCUMENT_START_EVENT) { ++ path->start_level = 1; ++ yaml_path_section_get_first(path)->valid = true; ++ } ++ break; ++ case YAML_PATH_SECTION_ANCHOR: ++ if (anchor != NULL && !strcmp(yaml_path_section_get_first(path)->data.anchor, anchor)) { ++ path->start_level = path->current_level; ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ yaml_path_section_t *current_section = yaml_path_section_get_current(path); ++ if (current_section) { ++ switch (event->type) { ++ case YAML_DOCUMENT_START_EVENT: ++ case YAML_MAPPING_START_EVENT: ++ case YAML_SEQUENCE_START_EVENT: ++ case YAML_ALIAS_EVENT: ++ case YAML_SCALAR_EVENT: ++ switch (current_section->node_type) { ++ case YAML_NO_NODE: ++ if (current_section->type == YAML_PATH_SECTION_ANCHOR) { ++ current_section->valid = false; ++ if (anchor != NULL && !strcmp(current_section->data.anchor, anchor)) ++ current_section->valid = true; ++ } ++ break; ++ case YAML_MAPPING_NODE: ++ if (current_section->type == YAML_PATH_SECTION_KEY) { ++ if (current_section->counter % 2) { ++ current_section->valid = current_section->next_valid; ++ current_section->next_valid = false; ++ } else { ++ current_section->next_valid = !strcmp(current_section->data.key, (const char *)event->data.scalar.value); ++ current_section->valid = false; ++ } ++ } else if (current_section->type == YAML_PATH_SECTION_SELECTION) { ++ if (current_section->counter % 2) { ++ current_section->valid = current_section->next_valid; ++ current_section->next_valid = false; ++ } else { ++ current_section->next_valid = yaml_path_selection_is_empty(¤t_section->data.selection) ++ || yaml_path_selection_key_get(¤t_section->data.selection, (const char *)event->data.scalar.value) != NULL; ++ current_section->valid = current_section->next_valid; ++ } ++ } else { ++ current_section->valid = false; ++ } ++ break; ++ case YAML_SEQUENCE_NODE: ++ if (current_section->type == YAML_PATH_SECTION_INDEX) { ++ current_section->valid = current_section->data.index == current_section->counter; ++ } else if (current_section->type == YAML_PATH_SECTION_SET) { ++ current_section->valid = yaml_path_set_is_empty(current_section->data.set) ++ || yaml_path_set_has_index(current_section->data.set, current_section->counter); ++ } else { ++ current_section->valid = false; ++ } ++ break; ++ default: ++ break; ++ } ++ current_section->counter++; ++ default: ++ break; ++ } ++ } ++ ++ switch (event->type) { ++ case YAML_STREAM_START_EVENT: ++ case YAML_STREAM_END_EVENT: ++ case YAML_NO_EVENT: ++ res = YAML_PATH_FILTER_RESULT_IN; ++ break; ++ case YAML_DOCUMENT_START_EVENT: ++ if (path->start_level == 1) ++ path->current_level++; ++ res = YAML_PATH_FILTER_RESULT_IN; ++ break; ++ case YAML_DOCUMENT_END_EVENT: ++ if (path->start_level == 1) ++ path->current_level--; ++ res = YAML_PATH_FILTER_RESULT_IN; ++ break; ++ case YAML_MAPPING_START_EVENT: ++ case YAML_SEQUENCE_START_EVENT: ++ if (current_section) { ++ if (yaml_path_section_current_is_last(path)) ++ if (yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } else { ++ if (path->current_level > path->start_level) { ++ if (yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } ++ } ++ path->current_level++; ++ current_section = yaml_path_section_get_current(path); ++ if (current_section) { ++ current_section->node_type = event->type == YAML_MAPPING_START_EVENT ? YAML_MAPPING_NODE : YAML_SEQUENCE_NODE; ++ current_section->counter = 0; ++ } ++ if (current_section) { ++ if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } ++ break; ++ case YAML_MAPPING_END_EVENT: ++ case YAML_SEQUENCE_END_EVENT: ++ if (current_section) { ++ if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ ++ } ++ path->current_level--; ++ current_section = yaml_path_section_get_current(path); ++ if (current_section) { ++ if (yaml_path_section_current_is_last(path)) ++ if (yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } else { ++ if (path->current_level > path->start_level) { ++ if (yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } ++ } ++ break; ++ case YAML_ALIAS_EVENT: ++ case YAML_SCALAR_EVENT: ++ if (!current_section) { ++ if (path->current_level >= path->start_level) ++ if (yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ } else { ++ if (yaml_path_section_current_is_last(path) && yaml_path_is_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN; ++ if (current_section->valid ++ && current_section->node_type == YAML_MAPPING_NODE ++ && current_section->counter % 2) { ++ if (yaml_path_section_is_mandatory_container(current_section) && yaml_path_sections_prev_are_valid(path)) ++ res = YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY; ++ } ++ } ++ break; ++ default: ++ break; ++ } ++ ++ return res; ++} +diff --git a/yaml-path.h b/src/yaml-path.h +similarity index 72% +rename from yaml-path.h +rename to src/yaml-path.h +index eb9e9dc..66e0298 100644 +--- a/yaml-path.h ++++ b/src/yaml-path.h +@@ -8,20 +8,22 @@ typedef struct yaml_path yaml_path_t; + + typedef enum yaml_path_error_type { + YAML_PATH_ERROR_NONE, ++ YAML_PATH_ERROR_NOMEM, + YAML_PATH_ERROR_PARSE, ++ YAML_PATH_ERROR_SECTION, + } yaml_path_error_type_t; + + typedef struct yaml_path_error { + yaml_path_error_type_t type; + const char *message; +- const char *context; + size_t pos; + } yaml_path_error_t; + +-typedef enum yaml_path_filter_mode { +- YAML_PATH_FILTER_RETURN_ALL, +- YAML_PATH_FILTER_RETURN_SHALLOW, +-} yaml_path_filter_mode_t; ++typedef enum yaml_path_filter_result { ++ YAML_PATH_FILTER_RESULT_OUT, ++ YAML_PATH_FILTER_RESULT_IN, ++ YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY, ++} yaml_path_filter_result_t; + + + yaml_path_t* +@@ -36,8 +38,8 @@ yaml_path_destroy (yaml_path_t *path); + const yaml_path_error_t* + yaml_path_error_get (yaml_path_t *path); + +-int +-yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event, yaml_path_filter_mode_t mode); ++yaml_path_filter_result_t ++yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event); + + size_t + yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len); +diff --git a/yamlp.c b/src/yamlp.c +similarity index 80% +rename from yamlp.c +rename to src/yamlp.c +index 334c626..f4d44d2 100644 +--- a/yamlp.c ++++ b/src/yamlp.c +@@ -9,13 +9,14 @@ + #include "yaml-path.h" + + +-int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t *path, yaml_path_filter_mode_t mode, int use_flow_style) ++static int ++parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t *path, int use_flow_style) + { + yaml_event_t event; +- yaml_event_type_t prev_event_type, event_type; ++ yaml_event_type_t event_type, prev_event_type = YAML_NO_EVENT; ++ yaml_path_filter_result_t result, prev_result = YAML_PATH_FILTER_RESULT_OUT; + + do { +- + if (!yaml_parser_parse(parser, &event)) { + switch (parser->error) { + case YAML_MEMORY_ERROR: +@@ -53,7 +54,8 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t + return 1; + } else { + event_type = event.type; +- if (!yaml_path_filter_event(path, parser, &event, mode)) { ++ result = yaml_path_filter_event(path, parser, &event); ++ if (result == YAML_PATH_FILTER_RESULT_OUT) { + yaml_event_delete(&event); + } else { + if (use_flow_style) { +@@ -68,11 +70,16 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t + break; + } + } +- if (prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) { ++ if ((prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) ++ || (prev_result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY ++ && (event_type == YAML_MAPPING_END_EVENT ++ || event_type == YAML_SEQUENCE_END_EVENT ++ || result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY))) { + yaml_event_t null_event= {0}; + yaml_scalar_event_initialize(&null_event, NULL, (yaml_char_t *)"!!null", (yaml_char_t *)"null", 4, 1, 0, YAML_ANY_SCALAR_STYLE); + yaml_emitter_emit(emitter, &null_event); + } ++ prev_result = result; + prev_event_type = event_type; + if (!yaml_emitter_emit(emitter, &event)) { + switch (emitter->error) +@@ -100,11 +107,12 @@ int parse_and_emit (yaml_parser_t *parser, yaml_emitter_t *emitter, yaml_path_t + } + + +-void help (void) ++static void ++help (void) + { + printf("yamlp - filtering utility for YAML documents\n"); + printf("\n"); +- printf("Usage: yamlp [-F] [-S] [-W ] [-f ] \n"); ++ printf("Usage: yamlp [-F] [-W ] [-f ] \n"); + printf(" yamlp -h\n"); + printf("\n"); + printf("The tool will take the input YAML document from or a (-f option),\n"); +@@ -118,8 +126,6 @@ void help (void) + printf("\n"); + printf(" -h help;\n"); + printf("\n"); +- printf(" -S 'shallow' filter mode;\n"); +- printf("\n"); + printf(" -W line wrap width, no wrapping if omitted.\n"); + printf("\n"); + } +@@ -129,8 +135,7 @@ int main(int argc, char *argv[]) + int flow = 0; + char *file_name = NULL; + char *path_string = NULL; +- int wrap = -1; +- yaml_path_filter_mode_t mode = YAML_PATH_FILTER_RETURN_ALL; ++ long wrap = -1; + + int opt; + while ((opt = getopt(argc, argv, ":f:W:vhSF")) != -1) { +@@ -138,13 +143,9 @@ int main(int argc, char *argv[]) + case 'h': + help(); + return 0; +- break; + case 'F': + flow = 1; + break; +- case 'S': +- mode = YAML_PATH_FILTER_RETURN_SHALLOW; +- break; + case 'W': + wrap = strtol(optarg, NULL, 10); + if (!wrap) { +@@ -158,11 +159,12 @@ int main(int argc, char *argv[]) + case ':': + fprintf(stderr, "Option needs a value\n"); + return 1; +- break; + case '?': + fprintf(stderr, "Unknown option '%c'\n", optopt); + return 1; +- break; ++ default: ++ fprintf(stderr, "Unhandled option '%c'\n", opt); ++ return 1; + } + } + +@@ -186,9 +188,10 @@ int main(int argc, char *argv[]) + + yaml_path_t *path = yaml_path_create(); + if (yaml_path_parse(path, path_string)) { +- fprintf(stderr, "Invalid path '%s' (%s)\n", path_string, yaml_path_error_get(path)->message); ++ fprintf(stderr, "Invalid path: '%s'\n", path_string); ++ fprintf(stderr, " %*s^ %s [at position %zu]\n", (int)yaml_path_error_get(path)->pos, " ", yaml_path_error_get(path)->message, yaml_path_error_get(path)->pos); + return 3; +- }; ++ } + + yaml_parser_t parser; + yaml_emitter_t emitter; +@@ -198,9 +201,9 @@ int main(int argc, char *argv[]) + + yaml_emitter_initialize(&emitter); + yaml_emitter_set_output_file(&emitter, stdout); +- yaml_emitter_set_width(&emitter, wrap); ++ yaml_emitter_set_width(&emitter, (int) wrap); + +- if (parse_and_emit(&parser, &emitter, path, mode, flow)) { ++ if (parse_and_emit(&parser, &emitter, path, flow)) { + return 4; + } + +@@ -208,7 +211,8 @@ int main(int argc, char *argv[]) + yaml_emitter_delete(&emitter); + + yaml_path_destroy(path); +- fclose(file); ++ if (file != NULL) ++ fclose(file); + + return 0; + } +diff --git a/test-yamlp.sh b/test-yamlp.sh +deleted file mode 100755 +index 8c2b413..0000000 +--- a/test-yamlp.sh ++++ /dev/null +@@ -1,19 +0,0 @@ +-#!/bin/bash +- +-yamlp_test() +-{ +- echo -n "$1 ($2) " +- out=$(./yamlp -F -f "$1" "$2") || return 1 +- echo -n "-> $out" +- if [ "$out" != "$3" ]; then +- echo ": FAILED, expected result: $3" +- return 2 +- else +- echo ": OK" +- fi +-} +- +-yamlp_test "../openshift-logging.yaml" ".spec.pipelines[:].inputSource" "[logs.app, logs.infra, logs.audit]" +-res=$((res+$?)) +- +-exit $res +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +new file mode 100644 +index 0000000..bc01b4b +--- /dev/null ++++ b/tests/CMakeLists.txt +@@ -0,0 +1,28 @@ ++function(_add_test TEST_NAME TEST_COMMAND) ++ add_test(NAME ${TEST_NAME} COMMAND ${TEST_COMMAND}) ++ set_tests_properties(${TEST_NAME} PROPERTIES ++ SKIP_RETURN_CODE 255 ++ ENVIRONMENT "CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR};SOURCE_DIR=${CMAKE_SOURCE_DIR};BINARY_DIR=${CMAKE_BINARY_DIR}" ++ ) ++endfunction() ++ ++function(add_test_executable EXECUTABLE_NAME SOURCE_FILE) ++ set(TEST_EXECUTABLE_LIBRARIES yaml-path) ++ add_executable(${EXECUTABLE_NAME} ${SOURCE_FILE} ${ARGN}) ++ target_link_libraries(${EXECUTABLE_NAME} ${TEST_EXECUTABLE_LIBRARIES}) ++ target_include_directories(${EXECUTABLE_NAME} PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") ++ add_coverage(${EXECUTABLE_NAME}) ++ string(REPLACE "${CMAKE_SOURCE_DIR}/tests/" "" TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${EXECUTABLE_NAME}") ++ _add_test(${TEST_NAME} "${CMAKE_BINARY_DIR}/tests/${EXECUTABLE_NAME}") ++endfunction() ++ ++function(add_test_script TEST_SCRIPT) ++ string(REPLACE "${CMAKE_SOURCE_DIR}/tests/" "" TEST_NAME "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SCRIPT}") ++ _add_test(${TEST_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_SCRIPT}") ++endfunction() ++ ++add_test_executable(test-path-segments test-path-segments.c) ++add_test_executable(test-paths test-paths.c) ++add_test_script(test-yamlp.sh) ++ ++list(APPEND LCOV_REMOVE_PATTERNS "'${CMAKE_SOURCE_DIR}/tests/*'") +diff --git a/test-path-segments.c b/tests/test-path-segments.c +similarity index 54% +rename from test-path-segments.c +rename to tests/test-path-segments.c +index 2340937..0889004 100644 +--- a/test-path-segments.c ++++ b/tests/test-path-segments.c +@@ -1,6 +1,7 @@ ++// SPDX-License-Identifier: MIT ++// Copyright (c) 2020 Red Hat Inc., Durham, North Carolina. ++ + #include +-#include +-#include + + #include "yaml-path.h" + +@@ -17,7 +18,7 @@ yp_s[PATH_STRING_LEN] = {0}; + #define ASCII_ERR "\033[0;33m" + #define ASCII_RST "\033[0;0m" + +-void ++static void + yp_test (char *p, int expected_failure) + { + yaml_path_t *yp = yaml_path_create(); +@@ -35,7 +36,7 @@ yp_test (char *p, int expected_failure) + printf(ASCII_ERR); + test_result++; + } +- printf(" -X %s (at pos: %zu): %s\n", ype->message, ype->pos, !expected_failure ? ASCII_RST"FAILED" : "OK"); ++ printf(" -- %s (at pos: %zu): %s\n", ype->message, ype->pos, !expected_failure ? ASCII_RST"FAILED" : "OK"); + } + yaml_path_destroy(yp); + } +@@ -46,7 +47,9 @@ yp_test (char *p, int expected_failure) + + int main (int argc, char *argv[]) + { +- yp_test_good(".first"); ++ (void) argc; (void) argv; // Yep, we don't need them ++ ++ yp_test_good(".first"); + yp_test_good(".first[0]"); + yp_test_good(".first.second[0].third"); + yp_test_good(".first.0"); +@@ -58,31 +61,62 @@ int main (int argc, char *argv[]) + yp_test_good("!"); + yp_test_good("$"); + +- yp_test_good("[0:0]"); +- yp_test_good("[0:0:1]"); +- yp_test_good("[100:]"); +- yp_test_good("[100::]"); +- yp_test_good("[:100]"); +- yp_test_good("[:100:]"); + yp_test_good("[:]"); +- yp_test_good("[::]"); +- yp_test_good("[-03:-200:+500]"); ++ yp_test_good("[':']['*'][:]"); ++ yp_test_good(".:.*[:]"); ++ yp_test_good("[0,2,3,4,5,20,180]"); + + yp_test_good("&anc"); + yp_test_good("&anc[0]"); + yp_test_good("&anc[0].zzz"); + + yp_test_good("el['key']"); +- yp_test_good("el['key'].other[0]['key']"); ++ yp_test_good("el[\"key\"]"); ++ yp_test_good("el[\"k[]ey\"]"); ++ yp_test_good("el[\"k'ey\"]"); ++ yp_test_good("el['k\"ey']"); ++ yp_test_good("el.k\"ey"); ++ yp_test_good("el.k$ey"); ++ yp_test_good("el.k'&'ey"); ++ yp_test_good("el['key'].other[0]['key'][0,2]"); ++ ++ yp_test_good("el['first','other']"); ++ yp_test_good("el[\"first\",\"other\"]"); ++ yp_test_good("el[\"first\",'other']"); ++ yp_test_good("el['key','valid']['now','allowed']"); ++ yp_test_good("el.*"); ++ yp_test_good("el[*]"); ++ yp_test_good("el['*']"); ++ ++ yp_test_invalid("$$"); ++ yp_test_invalid("$&"); ++ ++ yp_test_invalid("&"); + + yp_test_invalid("$."); + yp_test_invalid(""); + yp_test_invalid("."); + yp_test_invalid("element["); + ++ yp_test_invalid("[-5]"); ++ yp_test_invalid("[1,-5]"); ++ ++ yp_test_invalid("[0:0]"); ++ yp_test_invalid("[0:0:1]"); ++ yp_test_invalid("[100:]"); ++ yp_test_invalid("[100::]"); ++ yp_test_invalid("[:100]"); ++ yp_test_invalid("[:100:]"); ++ yp_test_invalid("[::]"); ++ yp_test_invalid("[-03:-200:+500]"); ++ + yp_test_invalid("[0:0:0]"); + yp_test_invalid("[::-1]"); + yp_test_invalid("[0.key[0]"); ++ yp_test_invalid("[1,]"); ++ yp_test_invalid("[,]"); ++ yp_test_invalid("[1,:]"); ++ yp_test_invalid("[1,2:]"); + + yp_test_invalid("el[&]"); + yp_test_invalid("el[&"); +@@ -93,9 +127,18 @@ int main (int argc, char *argv[]) + yp_test_invalid("el[&anchor][100]"); + + yp_test_invalid("el[']"); ++ yp_test_invalid("[*'"); + yp_test_invalid("el['key].wrong"); + yp_test_invalid("el['key.wrong"); + yp_test_invalid("el['key'"); ++ yp_test_invalid("el['key\"]"); ++ yp_test_invalid("el[\"key']"); ++ yp_test_invalid("el['k'ey']"); ++ ++ yp_test_invalid("el['key';'wrong']"); ++ yp_test_invalid("el['key',]"); ++ yp_test_invalid("el['key',invalid]"); ++ yp_test_invalid("el['first',]"); + + return test_result; + } +diff --git a/test-paths.c b/tests/test-paths.c +similarity index 65% +rename from test-paths.c +rename to tests/test-paths.c +index 58cfdad..fa722e8 100644 +--- a/test-paths.c ++++ b/tests/test-paths.c +@@ -1,6 +1,8 @@ ++// SPDX-License-Identifier: MIT ++// Copyright (c) 2020 Red Hat Inc., Durham, North Carolina. ++ + #include + #include +-#include + #include + + #include "yaml-path.h" +@@ -59,14 +61,11 @@ yaml_out[YAML_STRING_LEN] = {0}; + static size_t + yaml_out_len = 0; + +-static yaml_path_filter_mode_t +-mode = YAML_PATH_FILTER_RETURN_ALL; +- + static int + test_result = 0; + + +-int ++static int + yp_run (char *path) + { + yaml_parser_t parser; +@@ -74,11 +73,15 @@ yp_run (char *path) + int res = 0; + + yaml_path_t *yp = yaml_path_create(); +- yaml_path_parse(yp, path); ++ if (yaml_path_parse(yp, path)) { ++ printf("Path error: %s\n", yaml_path_error_get(yp)->message); ++ yaml_path_destroy(yp); ++ return 1; ++ } + +- char spath[YAML_STRING_LEN] = {0}; +- yaml_path_snprint(yp, spath, YAML_STRING_LEN); +- printf("(%s) ", spath); ++ //char spath[YAML_STRING_LEN] = {0}; ++ //yaml_path_snprint(yp, spath, YAML_STRING_LEN); ++ //printf("(%s) ", spath); + + yaml_emitter_initialize(&emitter); + yaml_parser_initialize(&parser); +@@ -89,7 +92,8 @@ yp_run (char *path) + yaml_emitter_set_width(&emitter, -1); + + yaml_event_t event; +- yaml_event_type_t prev_event_type, event_type; ++ yaml_event_type_t event_type, prev_event_type = YAML_NO_EVENT; ++ yaml_path_filter_result_t result, prev_result = 0; + + do { + if (!yaml_parser_parse(&parser, &event)) { +@@ -126,30 +130,35 @@ yp_run (char *path) + goto error; + } else { + event_type = event.type; +- if (!yaml_path_filter_event(yp, &parser, &event, mode)) { ++ result = yaml_path_filter_event(yp, &parser, &event); ++ if (result == YAML_PATH_FILTER_RESULT_OUT) { + yaml_event_delete(&event); + } else { +- if (prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) { ++ if ((prev_event_type == YAML_DOCUMENT_START_EVENT && event_type == YAML_DOCUMENT_END_EVENT) ++ || (prev_result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY ++ && (event_type == YAML_MAPPING_END_EVENT || event_type == YAML_SEQUENCE_END_EVENT || result == YAML_PATH_FILTER_RESULT_IN_DANGLING_KEY))) { + yaml_event_t null_event= {0}; + yaml_scalar_event_initialize(&null_event, NULL, (yaml_char_t *)"!!null", (yaml_char_t *)"null", 4, 1, 0, YAML_ANY_SCALAR_STYLE); + yaml_emitter_emit(&emitter, &null_event); + } ++ prev_result = result; + prev_event_type = event_type; + if (!yaml_emitter_emit(&emitter, &event)) { +- printf("Error after '%s'\n", yp_event_name(event.type)); ++ yaml_emitter_flush(&emitter); ++ printf("%s --> Error after '%s': ", yaml_out, yp_event_name(event.type)); + switch (emitter.error) + { + case YAML_MEMORY_ERROR: +- printf("Memory error: Not enough memory for emitting\n"); ++ printf("Memory error (Not enough memory for emitting)"); + break; + case YAML_WRITER_ERROR: +- printf("Writer error: %s\n", emitter.problem); ++ printf("Writer error (%s)", emitter.problem); + break; + case YAML_EMITTER_ERROR: +- printf("Emitter error: %s\n", emitter.problem); ++ printf("Emitter error (%s)", emitter.problem); + break; + default: +- printf("Internal error\n"); ++ printf("Internal error"); + break; + } + res = 2; +@@ -171,17 +180,19 @@ yp_run (char *path) + #define ASCII_ERR "\033[0;33m" + #define ASCII_RST "\033[0;0m" + +-void ++static void + yp_test (char *path, char *yaml_exp) + { +- printf("%s ", path); ++ printf("%s "ASCII_ERR, path); + if (!yp_run(path)) { + rstrip(yaml_out); + if (!strcmp(yaml_exp, yaml_out)) { +- printf("(%s): OK\n", yaml_exp); ++ printf(ASCII_RST"(%s): OK\n", yaml_exp); + return; + } +- printf(ASCII_ERR"(%s != %s)"ASCII_RST": FAILED\n", yaml_exp, yaml_out); ++ printf("(%s != %s)"ASCII_RST": FAILED\n", yaml_exp, yaml_out); ++ } else { ++ printf(ASCII_RST": ERROR\n"); + } + test_result++; + } +@@ -189,6 +200,8 @@ yp_test (char *path, char *yaml_exp) + + int main (int argc, char *argv[]) + { ++ (void) argc; (void) argv; // Yep, we don't need them ++ + yaml = + "{" + "first: {" +@@ -204,14 +217,18 @@ int main (int argc, char *argv[]) + "]" + "}," + "second: [" +- "{'abc': &anc [1, 2], 'abcdef': 2, 'z': *anc}," +- "{'abc': [3, 4], 'abcdef': 4, 'z': 'zzz'}" ++ "{'abc': &anc [1, 2], 'def': [11, 22], 'abcdef': 2, 'z': *anc, 'q': 'Q'}," ++ "{'abc': [3, 4], 'def': {'z': '!'}, 'abcdef': 4, 'z': 'zzz'}" ++ "]," ++ "3rd: [" ++ "{'a': {'A': [0, 1], 'AA': [2, 3]}, 'b': {'A': [10, 11], 'BB': [9, 8]}}," ++ "{'z': {'A': [0, 1], 'BB': [22, 33]}}," ++ "&x {'q': [1, 2]}," + "]" + "}"; + + // Path Expected filtered YAML result + +- mode = YAML_PATH_FILTER_RETURN_ALL; + yp_test("$.first.Map", "{1: '1'}"); + yp_test(".first", "{'Map': {1: '1'}, 'Nop': 0, 'Yep': '1', 'Arr': [[11, 12], 2, ['31', '32'], [4, 5, 6, 7, 8, 9], {'k': 'val', 0: 0}]}"); + yp_test(".first.Nop", "0"); +@@ -219,24 +236,27 @@ int main (int argc, char *argv[]) + yp_test(".first.Arr[0]", "[11, 12]"); + yp_test(".first.Arr[1]", "2"); + yp_test(".first.Arr[2][0]", "'31'"); +- yp_test(".first.Arr[:2][0]", "[11]"); + yp_test(".first.Arr[3][:]", "[4, 5, 6, 7, 8, 9]"); ++ yp_test(".first.Arr[:][:]", "[[11, 12], ['31', '32'], [4, 5, 6, 7, 8, 9]]"); + yp_test(".first.Arr[4].k", "'val'"); + yp_test(".first.Arr[:][0]", "[11, '31', 4]"); + yp_test(".first.Arr[:].k", "['val']"); + yp_test(".first.Arr[:][2]", "[6]"); +- yp_test(".first.Arr[3][1::2]", "[5, 7, 9]"); +- yp_test(".first.Arr[3][::2]", "[4, 6, 8]"); +- yp_test(".first.Arr[3][:4:2]", "[4, 6]"); ++ yp_test(".first.Arr[:][0,1]", "[[11, 12], ['31', '32'], [4, 5]]"); ++ yp_test(".first.Arr[:][1]", "[12, '32', 5]"); + yp_test(".second[2].abc", "null"); +- yp_test(".second[0:2].abc", "[&anc [1, 2], [3, 4]]"); + yp_test(".second[0].z", "*anc"); ++ yp_test("&anc", "&anc [1, 2]"); + yp_test("&anc[0]", "1"); +- +- mode = YAML_PATH_FILTER_RETURN_SHALLOW; +- yp_test(".first", "{}"); +- yp_test(".first.Nop", "0"); +- yp_test(".first.Map", "{}"); ++ yp_test(".first['Nop','Yep']", "{'Nop': 0, 'Yep': '1'}"); ++ yp_test(".second[0]['abc','def'][0]","{'abc': 1, 'def': 11}"); ++ yp_test(".second[:]['abc','def'][0]","[{'abc': 1, 'def': 11}, {'abc': 3, 'def': null}]"); ++ yp_test(".second[:]['abc','def'].z", "[{'abc': null, 'def': null}, {'abc': null, 'def': '!'}]"); ++ yp_test(".second[:][*].z", "[{'abc': null, 'def': null, 'abcdef': null, 'z': null, 'q': null}, {'abc': null, 'def': '!', 'abcdef': null, 'z': null}]"); ++ yp_test(".second[:]['abc','q']", "[{'abc': &anc [1, 2], 'q': 'Q'}, {'abc': [3, 4]}]"); ++ yp_test(".second[:]['abc','def'][:]","[{'abc': &anc [1, 2], 'def': [11, 22]}, {'abc': [3, 4], 'def': null}]"); ++ yp_test(".second[0]['abc','def']", "{'abc': &anc [1, 2], 'def': [11, 22]}"); ++ yp_test(".3rd[:].*.*[:]", "[{'a': {'A': [0, 1], 'AA': [2, 3]}, 'b': {'A': [10, 11], 'BB': [9, 8]}}, {'z': {'A': [0, 1], 'BB': [22, 33]}}, &x {'q': null}]"); + + return test_result; + } +diff --git a/tests/test-yamlp.sh b/tests/test-yamlp.sh +new file mode 100755 +index 0000000..d009659 +--- /dev/null ++++ b/tests/test-yamlp.sh +@@ -0,0 +1,27 @@ ++#!/bin/bash ++ ++# SPDX-License-Identifier: MIT ++# Copyright (c) 2020 Red Hat Inc., Durham, North Carolina. ++ ++yamlp_test() ++{ ++ echo "$1:" ++ echo -n " ($2) " ++ out=$("${BINARY_DIR:-../build}/yamlp" -F -f "$1" "$2") || return 1 ++ echo -n "-> $out" ++ if [ "$out" != "$3" ]; then ++ echo ": FAILED, expected result: $3" ++ return 2 ++ else ++ echo ": OK" ++ fi ++} ++ ++yamlp_test "${SOURCE_DIR:-..}/res/openshift-logging.yaml" ".spec.pipelines[:].inputSource" "[logs.app, logs.infra, logs.audit]" ++res=$((res+$?)) ++ ++yamlp_test "${SOURCE_DIR:-..}/res/openshift-upgradeable.yaml" ".status.conditions[:]['status','type']" \ ++ '[{status: "False", type: Degraded}, {status: "False", type: Progressing}, {status: "True", type: Available}, {status: "True", type: Upgradeable}]' ++res=$((res+$?)) ++ ++exit $res +diff --git a/yaml-path.c b/yaml-path.c +deleted file mode 100644 +index 00611ca..0000000 +--- a/yaml-path.c ++++ /dev/null +@@ -1,588 +0,0 @@ +-#include +-#include +-#include +-#include +-#include +- +-#include +- +-#include "yaml-path.h" +- +- +-#define YAML_PATH_MAX_SECTION_LEN 1024 +-#define YAML_PATH_MAX_SECTIONS 255 +-#define YAML_PATH_MAX_LEN YAML_PATH_MAX_SECTION_LEN * YAML_PATH_MAX_SECTIONS +- +- +-typedef enum yaml_path_section_type { +- YAML_PATH_SECTION_ROOT, +- YAML_PATH_SECTION_ANCHOR, +- YAML_PATH_SECTION_KEY, +- YAML_PATH_SECTION_INDEX, +- YAML_PATH_SECTION_SLICE, +-} yaml_path_section_type_t; +- +-typedef struct yaml_path_section { +- yaml_path_section_type_t type; +- int level; +- union { +- const char *key; +- const char *anchor; +- int index; +- struct {int start, end, stride;} slice; +- } data; +- TAILQ_ENTRY(yaml_path_section) entries; +- +- yaml_node_type_t node_type; +- int counter; +- bool valid; +- bool next_valid; +-} yaml_path_section_t; +- +-typedef TAILQ_HEAD(path_section_list, yaml_path_section) path_section_list_t; +- +-typedef struct yaml_path { +- path_section_list_t sections_list; +- size_t sections_count; +- size_t sequence_level; +- +- size_t current_level; +- size_t start_level; +- +- yaml_path_error_t error; +-} yaml_path_t; +- +- +-static void +-yaml_path_error_set (yaml_path_t *path, yaml_path_error_type_t error_type, const char *message, size_t pos) +-{ +- assert(path != NULL); +- path->error.type = error_type; +- path->error.message = message; +- path->error.pos = pos; +-} +- +-static void +-yaml_path_sections_remove (yaml_path_t *path) +-{ +- assert(path != NULL); +- while (!TAILQ_EMPTY(&path->sections_list)) { +- yaml_path_section_t *el = TAILQ_FIRST(&path->sections_list); +- TAILQ_REMOVE(&path->sections_list, el, entries); +- path->sections_count--; +- switch (el->type) { +- case YAML_PATH_SECTION_KEY: +- free((void *)el->data.key); +- break; +- case YAML_PATH_SECTION_ANCHOR: +- free((void *)el->data.anchor); +- break; +- case YAML_PATH_SECTION_SLICE: +- if (path->sequence_level == el->level) +- path->sequence_level = 0; +- break; +- default: +- break; +- } +- free(el); +- } +-} +- +-static yaml_path_section_t* +-yaml_path_section_create (yaml_path_t *path, yaml_path_section_type_t section_type) +-{ +- yaml_path_section_t *el = malloc(sizeof(*el)); +- assert(el != NULL); +- memset(el, 0, sizeof(*el)); +- path->sections_count++; +- el->level = path->sections_count; +- el->type = section_type; +- el->node_type = YAML_SCALAR_NODE; +- TAILQ_INSERT_TAIL(&path->sections_list, el, entries); +- if (el->type == YAML_PATH_SECTION_SLICE && !path->sequence_level) { +- path->sequence_level = el->level; +- } +- return el; +-} +- +-static size_t +-yaml_path_section_snprint (yaml_path_section_t *section, char *s, size_t max_len) +-{ +- assert(section != NULL); +- if (s == NULL) +- return -1; +- size_t len = 0; +- switch (section->type) { +- case YAML_PATH_SECTION_ROOT: +- len = snprintf(s, max_len, "$"); +- break; +- case YAML_PATH_SECTION_KEY: +- len = snprintf(s, max_len, ".%s", section->data.key); +- break; +- case YAML_PATH_SECTION_ANCHOR: +- len = snprintf(s, max_len, "&%s", section->data.anchor); +- break; +- case YAML_PATH_SECTION_INDEX: +- len = snprintf(s, max_len, "[%d]", section->data.index); +- break; +- case YAML_PATH_SECTION_SLICE: +- len = snprintf(s, max_len, "[%d:%d:%d]", section->data.slice.start, section->data.slice.end, section->data.slice.stride); +- break; +- default: +- len = snprintf(s, max_len, ""); +- break; +- } +- return len; +-} +- +-static void +-_parse (yaml_path_t *path, char *s_path) { +- char *sp = s_path; +- char *spe = NULL; +- +- assert(path != NULL); +- +- if (s_path == NULL || !s_path[0]) { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Path is empty", 0); +- return; +- } +- +- while (*sp != '\0') { +- switch (*sp) { +- case '.': +- case '[': +- if (path->sections_count == 0) { +- yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); +- } +- if (*sp == '.') { +- // Key +- spe = sp + 1; +- while (*spe != '.' && *spe != '[' && *spe != '\0') +- spe++; +- if (spe == sp+1) { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path); +- goto error; +- } +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); +- sec->data.key = strndup(sp + 1, spe-sp - 1); +- sp = spe-1; +- } else if (*sp == '[') { +- spe = sp+1; +- if (*spe == '\'') { +- // Key +- sp = spe; +- spe++; +- while (*spe != '\'' && *spe != '\0') +- spe++; +- if (spe == sp+1) { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is missing", sp - s_path); +- goto error; +- } +- if (*spe == '\0') { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (unxepected end of string, missing ''')", sp - s_path); +- goto error; +- } +- spe++; +- if (*spe == '\0') { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (unxepected end of string, missing ']')", sp - s_path); +- goto error; +- } +- if (*spe != ']') { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment key is invalid (invalid character)", spe - s_path); +- goto error; +- } +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); +- sec->data.key = strndup(sp + 1, spe-sp - 2); +- sp = spe; +- } else { +- // Index or Slice +- int idx = strtol(spe, &spe, 10); +- if (*spe == ']') { +- // Index +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_INDEX); +- sec->data.index = idx; +- sp = spe; +- } else if (*spe == ':') { +- // Slice +- int idx_start = idx; +- sp = spe++; +- idx = strtol(spe, &spe, 10); +- if (*spe == ':') { +- int idx_end = (spe == sp+1 ? __INT_MAX__ : idx); +- sp = spe++; +- idx = strtol(spe, &spe, 10); +- if (*spe == ']' && (idx > 0 || spe == sp+1)) { +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SLICE); +- sec->data.slice.start = idx_start; +- sec->data.slice.end = idx_end; +- sec->data.slice.stride = idx > 0 ? idx : 1; +- sp = spe; +- } else if (*spe == ']' && idx <= 0) { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice stride can not be less than 1", spe - s_path - 1); +- goto error; +- } else { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice stride is invalid (invalid character)", spe - s_path); +- goto error; +- } +- } else if (*spe == ']') { +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_SLICE); +- sec->data.slice.start = idx_start; +- sec->data.slice.end = (spe == sp+1 ? __INT_MAX__ : idx); +- sec->data.slice.stride = 1; +- sp = spe; +- } else { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment slice end index is invalid (invalid character)", spe - s_path); +- goto error; +- } +- } else if (*spe == '\0') { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment index is invalid (unxepected end of string, missing ']')", spe - s_path); +- goto error; +- } else { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Segment index is invalid (invalid character)", spe - s_path); +- goto error; +- } +- } +- } +- break; +- default: +- if (path->sections_count == 0) { +- spe = sp + 1; +- if (*sp == '$' && (*spe == '.' || *spe == '[' || *spe == '\0')) { +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); +- } else if (*sp == '&') { +- // Anchor +- sp++; +- while (*spe != '.' && *spe != '[' && *spe != '\0') +- spe++; +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ANCHOR); +- sec->data.anchor = strndup(sp, spe-sp); +- } else { +- // Key +- while (*spe != '.' && *spe != '[' && *spe != '\0') +- spe++; +- yaml_path_section_t *sec = yaml_path_section_create(path, YAML_PATH_SECTION_ROOT); +- sec = yaml_path_section_create(path, YAML_PATH_SECTION_KEY); +- sec->data.key = strndup(sp, spe-sp); +- } +- sp = spe-1; +- } +- break; +- } +- sp++; +- } +- +- if (path->sections_count == 0) { +- yaml_path_error_set(path, YAML_PATH_ERROR_PARSE, "Invalid path segments", 0); +- } +- +- return; +- +-error: +- yaml_path_sections_remove(path); +-} +- +-static yaml_path_section_t* +-yaml_path_section_get_at_level (yaml_path_t *path, size_t level) +-{ +- assert(path != NULL); +- yaml_path_section_t *el; +- TAILQ_FOREACH(el, &path->sections_list, entries) { +- if (el->level == level) +- return el; +- } +- return NULL; +-} +- +-static yaml_path_section_t* +-yaml_path_section_get_first (yaml_path_t *path) +-{ +- assert(path != NULL); +- return yaml_path_section_get_at_level(path, 1); +-} +- +-static yaml_path_section_t* +-yaml_path_section_get_current (yaml_path_t *path) +-{ +- assert(path != NULL); +- if (!path->start_level) +- return NULL; +- return yaml_path_section_get_at_level(path, path->current_level - path->start_level + 1); +-} +- +-static bool +-yaml_path_sections_prev_are_valid (yaml_path_t *path) +-{ +- assert(path != NULL); +- int valid = true; +- yaml_path_section_t *el; +- TAILQ_FOREACH(el, &path->sections_list, entries) { +- if (el->level < path->current_level - path->start_level + 1) +- valid = el->valid && valid; +- } +- return valid; +-} +- +-static bool +-yaml_path_section_current_is_last (yaml_path_t *path) +-{ +- assert(path != NULL); +- yaml_path_section_t *sec = yaml_path_section_get_current(path); +- if (sec == NULL) +- return false; +- return sec->level == path->sections_count; +-} +- +-static bool +-yaml_path_section_current_is_mandatory_sequence (yaml_path_t *path) +-{ +- assert(path != NULL); +- yaml_path_section_t *sec = yaml_path_section_get_current(path); +- if (sec == NULL) +- return false; +- return (sec->type == YAML_PATH_SECTION_SLICE && sec->level == path->sequence_level); +-} +- +-static bool +-yaml_path_is_valid (yaml_path_t *path) +-{ +- assert(path != NULL); +- bool valid = true; +- yaml_path_section_t *el; +- TAILQ_FOREACH(el, &path->sections_list, entries) { +- valid = el->valid && valid; +- } +- return valid; +-} +- +- +-/* Public */ +- +-yaml_path_t* +-yaml_path_create (void) +-{ +- yaml_path_t *ypath = malloc(sizeof(*ypath)); +- +- assert(ypath != NULL); +- memset (ypath, 0, sizeof(*ypath)); +- TAILQ_INIT(&ypath->sections_list); +- +- return ypath; +-} +- +-int +-yaml_path_parse (yaml_path_t *path, char *s_path) +-{ +- if (path == NULL) +- return -1; +- +- yaml_path_sections_remove(path); +- memset(&path->error, 0, sizeof(path->error)); +- +- _parse(path, s_path); +- +- if (path->sections_count == 0) +- return -2; +- +- return 0; +-} +- +-void +-yaml_path_destroy (yaml_path_t *path) +-{ +- if (path == NULL) +- return; +- yaml_path_sections_remove(path); +- free(path); +-} +- +-/* API */ +- +-const yaml_path_error_t* +-yaml_path_error_get (yaml_path_t *path) +-{ +- if (path == NULL) +- return NULL; +- return &path->error; +-} +- +-size_t +-yaml_path_snprint (yaml_path_t *path, char *s, size_t max_len) +-{ +- if (s == NULL) +- return -1; +- if (path == NULL) +- return 0; +- +- size_t len = 0; +- yaml_path_section_t *el; +- TAILQ_FOREACH(el, &path->sections_list, entries) { +- len += yaml_path_section_snprint(el, s + (len < max_len ? len : max_len), max_len - (len < max_len ? len : max_len)); +- } +- return len; +-} +- +-int +-yaml_path_filter_event (yaml_path_t *path, yaml_parser_t *parser, yaml_event_t *event, yaml_path_filter_mode_t mode) +-{ +- int res = 0; +- const char *anchor = NULL; +- +- if (path == NULL || parser == NULL || event == NULL) +- goto exit; +- +- switch(event->type) { +- case YAML_MAPPING_START_EVENT: +- anchor = (const char *)event->data.mapping_start.anchor; +- break; +- case YAML_SEQUENCE_START_EVENT: +- anchor = (const char *)event->data.sequence_start.anchor; +- break; +- case YAML_SCALAR_EVENT: +- anchor = (const char *)event->data.scalar.anchor; +- break; +- default: +- break; +- } +- +- if (!path->start_level) { +- switch (yaml_path_section_get_first(path)->type) { +- case YAML_PATH_SECTION_ROOT: +- if (event->type == YAML_DOCUMENT_START_EVENT) { +- path->start_level = 1; +- yaml_path_section_get_first(path)->valid = true; +- } +- break; +- case YAML_PATH_SECTION_ANCHOR: +- if (anchor != NULL) { +- if (!strcmp(yaml_path_section_get_first(path)->data.anchor, anchor)) { +- path->start_level = path->current_level; +- if (yaml_path_section_get_current(path)) +- yaml_path_section_get_current(path)->node_type = YAML_SCALAR_NODE; +- } +- } +- break; +- default: +- //TODO: This path is invalid +- break; +- } +- } else { +- //TODO: ? +- } +- +- yaml_path_section_t *current_section = yaml_path_section_get_current(path); +- if (!current_section) { +- } else { +- switch (event->type) { +- case YAML_DOCUMENT_START_EVENT: +- case YAML_MAPPING_START_EVENT: +- case YAML_SEQUENCE_START_EVENT: +- case YAML_ALIAS_EVENT: +- case YAML_SCALAR_EVENT: +- switch (current_section->node_type) { +- case YAML_SCALAR_NODE: +- current_section->valid = true; +- break; +- case YAML_MAPPING_NODE: +- if (current_section->type == YAML_PATH_SECTION_KEY) { +- if (current_section->counter % 2) { +- current_section->valid = current_section->next_valid; +- current_section->next_valid = false; +- } else { +- current_section->next_valid = !strcmp(current_section->data.key, (const char *)event->data.scalar.value); +- current_section->valid = false; +- } +- } else { +- current_section->valid = false; +- } +- break; +- case YAML_SEQUENCE_NODE: +- if (current_section->type == YAML_PATH_SECTION_INDEX) { +- current_section->valid = current_section->data.index == current_section->counter; +- } else if (current_section->type == YAML_PATH_SECTION_SLICE) { +- current_section->valid = current_section->data.slice.start <= current_section->counter && +- current_section->data.slice.end > current_section->counter && +- (current_section->data.slice.start + current_section->counter) % current_section->data.slice.stride == 0; +- } else { +- current_section->valid = false; +- } +- break; +- default: +- break; +- } +- current_section->counter++; +- default: +- break; +- } +- } +- +- switch (event->type) { +- case YAML_STREAM_START_EVENT: +- case YAML_STREAM_END_EVENT: +- case YAML_NO_EVENT: +- res = 1; +- break; +- case YAML_DOCUMENT_START_EVENT: +- if (path->start_level == 1) +- path->current_level++; +- res = 1; +- break; +- case YAML_DOCUMENT_END_EVENT: +- if (path->start_level == 1) +- path->current_level--; +- res = 1; +- break; +- case YAML_MAPPING_START_EVENT: +- case YAML_SEQUENCE_START_EVENT: +- if (current_section) { +- if (yaml_path_section_current_is_last(path)) +- res = yaml_path_is_valid(path); +- } else { +- if (path->current_level > path->start_level) { +- if (mode == YAML_PATH_FILTER_RETURN_ALL) +- res = yaml_path_is_valid(path); +- } +- } +- path->current_level++; +- current_section = yaml_path_section_get_current(path); +- if (current_section && yaml_path_section_current_is_mandatory_sequence(path)) { +- res = yaml_path_sections_prev_are_valid(path); +- } +- if (current_section) { +- current_section->node_type = event->type == YAML_MAPPING_START_EVENT ? YAML_MAPPING_NODE : YAML_SEQUENCE_NODE; +- current_section->counter = 0; +- } +- break; +- case YAML_MAPPING_END_EVENT: +- case YAML_SEQUENCE_END_EVENT: +- if (current_section) { +- if (yaml_path_section_current_is_mandatory_sequence(path)) +- res = yaml_path_sections_prev_are_valid(path); +- } +- path->current_level--; +- current_section = yaml_path_section_get_current(path); +- if (current_section) { +- if (yaml_path_section_current_is_last(path)) +- res = yaml_path_is_valid(path); +- } else { +- if (path->current_level > path->start_level) { +- if (mode == YAML_PATH_FILTER_RETURN_ALL) +- res = yaml_path_is_valid(path); +- } +- } +- break; +- case YAML_ALIAS_EVENT: +- case YAML_SCALAR_EVENT: +- if (!current_section) { +- if ((mode == YAML_PATH_FILTER_RETURN_ALL && path->current_level > path->start_level) || path->current_level == path->start_level) +- res = yaml_path_is_valid(path); +- } else { +- res = yaml_path_is_valid(path) && yaml_path_section_current_is_last(path); +- } +- break; +- default: +- break; +- } +- +-exit: +- return res; +-} +diff --git a/yaml.c b/yaml.c +deleted file mode 100644 +index e2d9007..0000000 +--- a/yaml.c ++++ /dev/null +@@ -1,172 +0,0 @@ +-#include +-#include +-#include +-#include +- +-#include "yaml-path.h" +- +-#define INDENT " " +-#define STRVAL(x) ((x) ? (char*)(x) : "") +- +-void indent(int level) +-{ +- int i; +- for (i = 0; i < level; i++) { +- printf("%s", INDENT); +- } +-} +- +-void print_event(yaml_event_t *event) +-{ +- static int level = 0; +- +- switch (event->type) { +- case YAML_NO_EVENT: +- indent(level); +- printf("no-event\n"); +- break; +- case YAML_STREAM_START_EVENT: +- indent(level++); +- printf("stream-start-event\n"); +- break; +- case YAML_STREAM_END_EVENT: +- indent(--level); +- printf("stream-end-event\n"); +- break; +- case YAML_DOCUMENT_START_EVENT: +- indent(level++); +- printf("document-start-event\n"); +- break; +- case YAML_DOCUMENT_END_EVENT: +- indent(--level); +- printf("document-end-event\n"); +- break; +- case YAML_ALIAS_EVENT: +- indent(level); +- printf("alias-event * (anc=\"%s\")\n", STRVAL(event->data.scalar.anchor)); +- break; +- case YAML_SCALAR_EVENT: +- indent(level); +- printf("= scalar-event (anc=\"%s\" val=\"%s\", l=%d, t=%s, pl_impl=%d, q_impl=%d, st=%d)\n", +- STRVAL(event->data.scalar.anchor), +- STRVAL(event->data.scalar.value), +- (int)event->data.scalar.length, +- event->data.scalar.tag, +- event->data.scalar.plain_implicit, event->data.scalar.quoted_implicit, event->data.scalar.style); +- break; +- case YAML_SEQUENCE_START_EVENT: +- indent(level++); +- printf("[ sequence-start-event (anc=\"%s\", t=%s)\n", +- STRVAL(event->data.sequence_start.anchor), +- event->data.sequence_start.tag); +- break; +- case YAML_SEQUENCE_END_EVENT: +- indent(--level); +- printf("] sequence-end-event\n"); +- break; +- case YAML_MAPPING_START_EVENT: +- indent(level++); +- printf("{ mapping-start-event\n"); +- break; +- case YAML_MAPPING_END_EVENT: +- indent(--level); +- printf("} mapping-end-event\n"); +- break; +- } +- if (level < 0) { +- fprintf(stderr, "indentation underflow!\n"); +- level = 0; +- } +-} +- +-int yaml_parser_parse_and_filter (yaml_parser_t *parser, yaml_event_t *event, yaml_path_t *path) +-{ +- int valid_event = 0; +- int res; +- do { +- res = yaml_parser_parse(parser, event); +- if (res) { +- printf("=====> "); +- print_event(event); +- if (!yaml_path_filter_event(path, parser, event, YAML_PATH_FILTER_RETURN_ALL)) { +- yaml_event_delete(event); +- } else { +- printf("+------------------------------------------------------------------------------------> "); +- print_event(event); +- valid_event = 1; +- } +- } else { +- break; +- } +- } while (!valid_event && res); +- +- return res; +-} +- +-int main(int argc, char *argv[]) +-{ +- yaml_parser_t parser; +- yaml_event_t event; +- yaml_event_type_t event_type; +- +- yaml_path_t *yp = yaml_path_create(); +- //yaml_path_parse(yp, ".fruit.Oop[1]"); +- //yaml_path_parse(yp, ".first.Arr[:2][0]"); //.Arr[2][0] +- //yaml_path_parse(yp, ".first.Arr[3][:]"); +- //yaml_path_parse(yp, ".first.Map"); +- //yaml_path_parse(yp, ".first.Arr[:].k"); +- //yaml_path_parse(yp, ".first.Arr[:][2]"); +- //yaml_path_parse(yp, ".metadata.name"); +- //yaml_path_parse(yp, ".spec.outputs[0:2].name"); +- //yaml_path_parse(yp, ".second[0].abc"); +- //yaml_path_parse(yp, "&anc[0]"); +- yaml_path_parse(yp, ".first.Map"); +- +- //const char *yaml = "2"; +- //const char *yaml = "{'el': {'Z': &anc [{'key': 0}, {'item': 1}]}, first: {'Map': &anc {1: '1'}, 'Nop': 'b', 'Yep': '2', 'Arr': [[11,12],2,[31,32],[4, 5, 6],{'k': 1, 0: 0}]}}"; +- const char *yaml = +- "{" +- "first: {" +- "'Map': {1: '1'}," +- "'Nop': 0," +- "'Yep': '1'," +- "'Arr': [" +- "[11, 12]," +- "2," +- "['31', '32']," +- "[4, 5, 6, 7, 8, 9]," +- "{'k': 'val', 0: 0}" +- "]" +- "}," +- "second: [" +- "{'abc': &anc [1, 2], 'abcdef': 2, 'z': *anc}," +- "{'abc': [3, 4], 'abcdef': 4, 'z': 'zzz'}" +- "]" +- "}"; +- +- char ypath[255] = {0}; +- yaml_path_snprint (yp, ypath, 255); +- printf("%s\n", yaml); +- printf("%s\n\n", ypath); +- +- yaml_parser_initialize(&parser); +- //yaml_parser_set_input_file(&parser, fopen("../openshift-logging-1.yaml", "r")); +- yaml_parser_set_input_string(&parser, (const unsigned char*)yaml, strlen(yaml)); +- +- do { +- if (!yaml_parser_parse_and_filter(&parser, &event, yp)) +- goto error; +- event_type = event.type; +- yaml_event_delete(&event); +- } while (event_type != YAML_STREAM_END_EVENT); +- +- yaml_path_destroy(yp); +- yaml_parser_delete(&parser); +- return 0; +- +-error: +- yaml_path_destroy(yp); +- fprintf(stderr, "Failed to parse: %s\n", parser.problem); +- yaml_parser_delete(&parser); +- return 1; +-} diff --git a/SOURCES/openscap-1.3.4-export-profile-platform-PR_1609.patch b/SOURCES/openscap-1.3.4-export-profile-platform-PR_1609.patch new file mode 100644 index 0000000..b25f114 --- /dev/null +++ b/SOURCES/openscap-1.3.4-export-profile-platform-PR_1609.patch @@ -0,0 +1,107 @@ +From cca0af9f2260a34aa4c2e57a7a418ce2b4732e16 Mon Sep 17 00:00:00 2001 +From: Watson Sato +Date: Mon, 28 Sep 2020 12:40:16 +0200 +Subject: [PATCH 1/2] Test resolving a Profile with platform + +--- + tests/API/XCCDF/unittests/CMakeLists.txt | 1 + + .../test_xccdf_resolve_profile_platform.sh | 31 +++++++++++++++++++ + ...t_xccdf_resolve_profile_platform.xccdf.xml | 13 ++++++++ + 3 files changed, 45 insertions(+) + create mode 100755 tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.sh + create mode 100644 tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.xccdf.xml + +diff --git a/tests/API/XCCDF/unittests/CMakeLists.txt b/tests/API/XCCDF/unittests/CMakeLists.txt +index 05ddea219..153a1c321 100644 +--- a/tests/API/XCCDF/unittests/CMakeLists.txt ++++ b/tests/API/XCCDF/unittests/CMakeLists.txt +@@ -62,6 +62,7 @@ add_oscap_test("test_default_selector.sh") + add_oscap_test("test_inherit_selector.sh") + add_oscap_test("test_xccdf_refine_value_bad.sh") + add_oscap_test("test_xccdf_resolve.sh") ++add_oscap_test("test_xccdf_resolve_profile_platform.sh") + add_oscap_test("test_xccdf_results_arf_no_oval.sh") + add_oscap_test("test_xccdf_sub_title.sh") + add_oscap_test("test_xccdf_test_system.sh") +diff --git a/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.sh b/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.sh +new file mode 100755 +index 000000000..95f8ce4b4 +--- /dev/null ++++ b/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.sh +@@ -0,0 +1,31 @@ ++#!/usr/bin/env bash ++. $builddir/tests/test_common.sh ++ ++######################################################################## ++### Test "oscap xccdf resolve" command on a Profile with platform ++######################################################################## ++ ++set -e ++set -o pipefail ++ ++name=$(basename $0 .sh) ++ ++result=$(mktemp -t ${name}.res.XXXXXX) ++stderr=$(mktemp -t ${name}.out.XXXXXX) ++stdout=$(mktemp -t ${name}.out.XXXXXX) ++ ++ ++echo "Stderr file = $stderr" ++echo "Result file = $result" ++ ++$OSCAP xccdf resolve --output $result $srcdir/${name}.xccdf.xml > $stdout ++$OSCAP xccdf validate $result >> $stdout ++ ++assert_exists 1 '//Benchmark[@resolved="1"]' ++ ++# Resolve Profile Platform ++assert_exists 2 '//Profile[@id="xccdf_resolve_profile_platform"]/select' ++assert_exists 1 '//Profile[@id="xccdf_resolve_profile_platform"]/platform[@idref="cpe:/a:open-scap:oscap"]' ++ ++[ -f $stderr ]; [ ! -s $stderr ]; rm $stderr ++rm $result +diff --git a/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.xccdf.xml b/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.xccdf.xml +new file mode 100644 +index 000000000..f4773bef7 +--- /dev/null ++++ b/tests/API/XCCDF/unittests/test_xccdf_resolve_profile_platform.xccdf.xml +@@ -0,0 +1,13 @@ ++ ++ ++ incomplete ++ 1.0 ++ ++ ++ Profile with platform ++ ++ ++ ++ ++ + +From 46b78146db6ba1fa57926068c4400d876423126b Mon Sep 17 00:00:00 2001 +From: Watson Sato +Date: Mon, 28 Sep 2020 13:03:46 +0200 +Subject: [PATCH 2/2] Fix export of platform profile to DOM + +The xccdf:platform should reference the ID of a CPE name or a CPE +applicability language expression. +--- + src/XCCDF/profile.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/XCCDF/profile.c b/src/XCCDF/profile.c +index 776ef616a..b8a3f4749 100644 +--- a/src/XCCDF/profile.c ++++ b/src/XCCDF/profile.c +@@ -319,7 +319,8 @@ void xccdf_profile_to_dom(struct xccdf_profile *profile, xmlNode *profile_node, + struct oscap_string_iterator *platforms = xccdf_profile_get_platforms(profile); + while (oscap_string_iterator_has_more(platforms)) { + const char *platform = oscap_string_iterator_next(platforms); +- xmlNewTextChild(profile_node, ns_xccdf, BAD_CAST "platform", BAD_CAST platform); ++ xmlNode *platform_node = xmlNewTextChild(profile_node, ns_xccdf, BAD_CAST "platform", NULL); ++ xmlNewProp(platform_node, BAD_CAST "idref", BAD_CAST platform); + } + oscap_string_iterator_free(platforms); + diff --git a/SOURCES/openscap-1.3.5-bump-yamlfilter-covscan-fix-PR_1620.patch b/SOURCES/openscap-1.3.5-bump-yamlfilter-covscan-fix-PR_1620.patch new file mode 100644 index 0000000..961d583 --- /dev/null +++ b/SOURCES/openscap-1.3.5-bump-yamlfilter-covscan-fix-PR_1620.patch @@ -0,0 +1,20 @@ +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 6f18abf83..e7e6cb3f5 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -216,12 +216,13 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + result_error("YAML parser error: %s", parser.problem); + goto cleanup; + } ++ ++ event_type = event.type; ++ + if (yaml_path_filter_event(yaml_path, &parser, &event) == YAML_PATH_FILTER_RESULT_OUT) { + goto next; + } + +- event_type = event.type; +- + if (sequence) { + if (event_type == YAML_SEQUENCE_END_EVENT) { + sequence = false; diff --git a/SOURCES/openscap-1.3.5-bump-yamlfilter-fix-field-names-PR_1619.patch b/SOURCES/openscap-1.3.5-bump-yamlfilter-fix-field-names-PR_1619.patch new file mode 100644 index 0000000..618a71d --- /dev/null +++ b/SOURCES/openscap-1.3.5-bump-yamlfilter-fix-field-names-PR_1619.patch @@ -0,0 +1,315 @@ +From 81ab472c579072229a61df32969cc027b0fa4b7f Mon Sep 17 00:00:00 2001 +From: Evgeny Kolesnikov +Date: Tue, 20 Oct 2020 08:55:32 +0200 +Subject: [PATCH] probes/yamfilecontent: Fix field names for cases where key + selection section is followed by a set section + +$.foo[:].bar[:], $.foo[:][:] and alike. +--- + .../independent/yamlfilecontent_probe.c | 31 ++++++++-- + .../yamlfilecontent/openshift-logging.yaml | 12 ++++ + .../test_probes_yamlfilecontent_array.sh | 2 +- + .../test_probes_yamlfilecontent_array.xml | 45 ++++++++++++++ + .../test_probes_yamlfilecontent_key.sh | 2 +- + .../test_probes_yamlfilecontent_key.xml | 59 ++++++++++++++++++- + 6 files changed, 143 insertions(+), 8 deletions(-) + +diff --git a/src/OVAL/probes/independent/yamlfilecontent_probe.c b/src/OVAL/probes/independent/yamlfilecontent_probe.c +index 6f18abf83..17741a240 100644 +--- a/src/OVAL/probes/independent/yamlfilecontent_probe.c ++++ b/src/OVAL/probes/independent/yamlfilecontent_probe.c +@@ -206,6 +206,7 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + yaml_event_type_t event_type; + bool sequence = false; + bool mapping = false; ++ bool fake_mapping = false; + int index = 0; + char *key = strdup("#"); + +@@ -224,21 +225,39 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + + if (sequence) { + if (event_type == YAML_SEQUENCE_END_EVENT) { +- sequence = false; ++ if (fake_mapping) { ++ fake_mapping = false; ++ if (record && record->itemcount > 0) { ++ oscap_list_add(values, record); ++ } else { ++ // Do not collect empty records ++ oscap_htable_free0(record); ++ } ++ record = NULL; ++ } else { ++ sequence = false; ++ } + } else if (event_type == YAML_SEQUENCE_START_EVENT) { +- result_error("YAML path '%s' points to a multi-dimensional structure (sequence containing another sequence)", yaml_path_cstr); +- goto cleanup; ++ if (mapping || fake_mapping) { ++ result_error("YAML path '%s' points to a multi-dimensional structure (a map or a sequence containing other sequences)", yaml_path_cstr); ++ goto cleanup; ++ } else { ++ fake_mapping = true; ++ record = oscap_htable_new(); ++ } + } + } else { + if (event_type == YAML_SEQUENCE_START_EVENT) { + sequence = true; ++ if (mapping) ++ index++; + } + } + + if (mapping) { + if (event_type == YAML_MAPPING_END_EVENT) { + mapping = false; +- if (record->itemcount > 0) { ++ if (record && record->itemcount > 0) { + oscap_list_add(values, record); + } else { + // Do not collect empty records +@@ -255,6 +274,10 @@ static int yaml_path_query(const char *filepath, const char *yaml_path_cstr, str + result_error("YAML path '%s' points to an invalid structure (map containing another map)", yaml_path_cstr); + goto cleanup; + } ++ if (fake_mapping) { ++ result_error("YAML path '%s' points to a multi-dimensional structure (two-dimensional sequence containing a map)", yaml_path_cstr); ++ goto cleanup; ++ } + mapping = true; + sequence = false; + index = 0; +diff --git a/tests/probes/yamlfilecontent/openshift-logging.yaml b/tests/probes/yamlfilecontent/openshift-logging.yaml +index fb6a9d8b6..581a700a3 100644 +--- a/tests/probes/yamlfilecontent/openshift-logging.yaml ++++ b/tests/probes/yamlfilecontent/openshift-logging.yaml +@@ -3,6 +3,18 @@ kind: "LogForwarding" + metadata: + name: instance + namespace: openshift-logging ++arrs: ++- [1, 2, 3] ++- [4, 5, 6] ++items: ++- allowHostDirVolumePlugin: false ++ defaultAddCapabilities: null ++ requiredDropCapabilities: ['KILL', 'ALL'] ++ name: ['Name', 'Oth'] ++- allowHostDirVolumePlugin: false ++ defaultAddCapabilities: null ++ requiredDropCapabilities: ['OPS', 'KILL', 'ALL'] ++ name: ['2 Name', '2 Oth'] + spec: + disableDefaultForwarding: true + outputs: +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.sh +index fd5e47538..695a247b3 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.sh +@@ -19,7 +19,7 @@ function test_probes_yamlfilecontent_array { + $OSCAP oval eval --results $RF $DF + + if [ -f $RF ]; then +- verify_results "def" $DF $RF 2 && verify_results "tst" $DF $RF 3 ++ verify_results "def" $DF $RF 3 && verify_results "tst" $DF $RF 5 + ret_val=$? + else + ret_val=1 +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +index c05c5fbb9..77f57cd47 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_array.xml +@@ -31,6 +31,17 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -49,6 +60,16 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -71,6 +92,18 @@ + .spec.outputs[0] + + ++ ++ /tmp ++ openshift-logging.yaml ++ .arrs[:][:] ++ ++ ++ ++ /tmp ++ openshift-logging.yaml ++ .arrs ++ ++ + + + +@@ -87,6 +120,12 @@ + + + ++ ++ ++ ++ ++ ++ + + + +@@ -99,5 +138,11 @@ + + + ++ ++ ++ 1|2|3|4|5|6 ++ ++ ++ + + +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +index fc1e0ae7e..a942552e9 100755 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.sh +@@ -19,7 +19,7 @@ function test_probes_yamlfilecontent_key { + $OSCAP oval eval --results $RF $DF + + if [ -f $RF ]; then +- verify_results "def" $DF $RF 6 && verify_results "tst" $DF $RF 7 ++ verify_results "def" $DF $RF 9 && verify_results "tst" $DF $RF 10 + ret_val=$? + else + ret_val=1 +diff --git a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +index 05757d0c8..1697b54fd 100644 +--- a/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml ++++ b/tests/probes/yamlfilecontent/test_probes_yamlfilecontent_key.xml +@@ -71,7 +71,7 @@ + + + +- ++ + + + +@@ -80,6 +80,26 @@ + + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -116,9 +136,19 @@ + + + +- ++ + + ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ + + + +@@ -170,6 +200,18 @@ + openshift-logging.yaml + .spec.outputs + ++ ++ ++ /tmp ++ openshift-logging.yaml ++ .items[:]['requiredDropCapabilities','name','q','z'][:] ++ ++ ++ ++ /tmp ++ openshift-logging.yaml ++ .items[:].requiredDropCapabilities[:] ++ + + + +@@ -202,6 +244,19 @@ + + + ++ ++ ++ ^KILL$ ++ Name ++ ++ ++ ++ ++ ++ ^KILL$ ++ ++ ++ + + + diff --git a/SPECS/openscap.spec b/SPECS/openscap.spec index 2536e77..1410370 100644 --- a/SPECS/openscap.spec +++ b/SPECS/openscap.spec @@ -1,6 +1,6 @@ Name: openscap Version: 1.3.3 -Release: 5%{?dist} +Release: 6%{?dist} Summary: Set of open source libraries enabling integration of the SCAP line of standards Group: System Environment/Libraries License: LGPLv2+ @@ -8,11 +8,17 @@ URL: http://www.open-scap.org/ Source0: https://github.com/OpenSCAP/%{name}/releases/download/%{version}/%{name}-%{version}.tar.gz Patch1: openscap-1.3.4-fix-environmentvariable58-regression.patch Patch2: openscap-1.3.4-fix-no-more-recursion.patch -Patch3: openscap-1.3.4-add_compression_support-PR_1557.patch -Patch4: openscap-1.3.4-add_compression_test-PR_1564.patch -Patch5: openscap-1.3.4-add_compression_tracing-PR_1561.patch +Patch3: openscap-1.3.4-add_compression_support-PR_1557.patch +Patch4: openscap-1.3.4-add_compression_test-PR_1564.patch +Patch5: openscap-1.3.4-add_compression_tracing-PR_1561.patch Patch6: openscap-1.3.4-rpmverifyfile_leak-PR_1565.patch Patch7: openscap-1.3.4-detect_remote_file_systems-PR_1573.patch +Patch8: openscap-1.3.4-export-profile-platform-PR_1609.patch +Patch9: openscap-1.3.4-bump-yamlfilter-fix-warnings-PR_1530.patch +Patch10: openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-PR_1552.patch +Patch11: openscap-1.3.4-bump-yamlfilter-upgrade-probe-schemas-submodule-PR_1552.patch +Patch12: openscap-1.3.5-bump-yamlfilter-covscan-fix-PR_1620.patch +Patch13: openscap-1.3.5-bump-yamlfilter-fix-field-names-PR_1619.patch BuildRequires: cmake >= 2.6 BuildRequires: swig libxml2-devel libxslt-devel perl-generators perl-XML-Parser @@ -140,6 +146,12 @@ for developing applications that use %{name}-engine-sce. %patch5 -p1 %patch6 -p1 %patch7 -p1 +%patch8 -p1 +%patch9 -p1 +%patch10 -p1 +%patch11 -p1 -d yaml-filter +%patch12 -p1 +%patch13 -p1 mkdir build %build @@ -227,6 +239,10 @@ rm -rf $RPM_BUILD_ROOT %{_bindir}/oscap-run-sce-script %changelog +* Thu Oct 29 2020 Evgeny Kolesnikov - 1.3.3-6 +- Enable profile composition with a specific platform (RHBZ#1896676) +- Enable YAML probe to work with sets of values (RHBZ#1895715) + * Wed Aug 19 2020 Jan Černý - 1.3.3-5 - Detect remote file systems correctly (RHBZ#1870087)