From e18f163d78533abf1617a8981668470b0f5d04c4 Mon Sep 17 00:00:00 2001 From: brarcher Date: Mon, 14 Sep 2015 01:25:13 +0000 Subject: [PATCH] Emit only valid XML characters Previously output XML files could contain illegal characters. Some of these characters could be encoded, and others should not be output at all. This change will attempt to encode characters if they are not printable or are special. Note that if a test contains a UTF-8 charater it will not be printed out accurately. In order to get the tests for encoding XML characters to work, the characters separating fields in a shell variable needed to be changed. Originally white space would separate fields, meaning shell would eat the extra whitespace in the tests. Because of the change, the xml output test needed to be reworked to not rely on white space field separators. Additionally, the printf program is used to properly un-escape strings used in the tests. git-svn-id: svn+ssh://svn.code.sf.net/p/check/code/trunk@1225 64e312b2-a51f-0410-8e61-82d0ca0eb02a --- NEWS | 3 ++ src/check_print.c | 65 +++++++++++++++++---------- tests/ex_output.c | 6 +-- tests/test_output_strings | 93 +++++++++++++++++++++------------------ tests/test_xml_output.sh | 18 +++++--- 5 files changed, 109 insertions(+), 76 deletions(-) diff --git a/NEWS b/NEWS index e69ac155..7eebca04 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ In Development: # Mentioning Check 0.10.0 for now, to fix distcheck target until next release +* Emit only valid XML characters in XML logging (assumes ASCII encoding). + Bug #103 + * Add LGPL header to files where it was missing; update FSF address in LGPL headers Bug #110 diff --git a/src/check_print.c b/src/check_print.c index d56e1707..35f13b64 100644 --- a/src/check_print.c +++ b/src/check_print.c @@ -95,34 +95,53 @@ static void srunner_fprint_results(FILE * file, SRunner * sr, void fprint_xml_esc(FILE * file, const char *str) { + /* The valid XML characters are as follows: + * #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] + * Characters that are outside of ASCII must be encoded. Further, the + * following special characters: + * " ' < > & + * must be encoded. We assume that the incoming string may be a multibyte + * character. + */ + for(; *str != '\0'; str++) { + char next = *str; - switch (*str) + /* handle special characters that must be escaped */ + if(next == '"' || next == '\'' || next == '<' || next == '>' || next == '&') { - - /* handle special characters that must be escaped */ - case '"': - fputs(""", file); - break; - case '\'': - fputs("'", file); - break; - case '<': - fputs("<", file); - break; - case '>': - fputs(">", file); - break; - case '&': - fputs("&", file); - break; - - /* regular characters, print as is */ - default: - fputc(*str, file); - break; + switch (next) + { + case '"': + fputs(""", file); + break; + case '\'': + fputs("'", file); + break; + case '<': + fputs("<", file); + break; + case '>': + fputs(">", file); + break; + case '&': + fputs("&", file); + break; + } + } + /* printable ASCII */ + else if(next >= ' ' && next <= '~') + { + fputc(next, file); + } + /* Non-printable character */ + else if(next == 0x9 || next == 0xA || next == 0xD || + next >= 0x20) + { + fprintf(file, "&#x%X;", next); } + /* If it did not get printed, it is not a valid XML character*/ } } diff --git a/tests/ex_output.c b/tests/ex_output.c index 6d0aad27..9e4e7467 100644 --- a/tests/ex_output.c +++ b/tests/ex_output.c @@ -75,7 +75,7 @@ END_TEST START_TEST(test_xml_esc_fail_msg) { - ck_abort_msg("fail \" ' < > & message"); + ck_abort_msg("fail \" ' < > & \x9 \xA" "X""\x08"" message"); /* backspace char \x08 deletes the X */ } END_TEST @@ -120,8 +120,8 @@ static Suite *make_xml_esc_suite(void) Suite *s; TCase *tc; - s = suite_create("XML escape \" ' < > & tests"); - tc = tcase_create("description \" ' < > &"); + s = suite_create("XML escape \" ' < > & \x9 \xA" "X""\x08"" tests"); /* backspace char \x08 deletes the X */ + tc = tcase_create("description \" ' < > & \x9 \xA" "X""\x08"" end"); /* backspace char \x08 deletes the X */ suite_add_tcase(s, tc); tcase_add_test(tc, test_xml_esc_fail_msg); diff --git a/tests/test_output_strings b/tests/test_output_strings index b23da03f..ccb3861f 100644 --- a/tests/test_output_strings +++ b/tests/test_output_strings @@ -2,12 +2,17 @@ . ./test_vars +# Set the 'internal field separator' character to +# something besides whitespace so that the string +# comparisons will work +IFS="~" + ################## # stdout output ################## -suite_output="Running suite(s): S1 +suite_output=`printf "Running suite(s): S1 S2 - XML escape \" ' < > & tests" + XML escape \" ' < > & \x9 \x0AX\x08 tests"` exp_silent="" @@ -20,25 +25,25 @@ exp_minimal="$suite_output $exp_minimal_result" if [ $HAVE_FORK -eq 1 ]; then -exp_normal_result="37%: Checks: 8, Failures: 4, Errors: 1 +exp_normal_result=`printf "37%%: Checks: 8, Failures: 4, Errors: 1 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1 ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message" +${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"` else -exp_normal_result="42%: Checks: 7, Failures: 4, Errors: 0 +exp_normal_result=`printf "42%%: Checks: 7, Failures: 4, Errors: 0 ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message" +${SRCDIR}ex_output.c:78:F:description \" ' < > &\x9 \xA end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"` fi exp_normal="$suite_output $exp_normal_result" if [ $HAVE_FORK -eq 1 ]; then -exp_verbose_result="37%: Checks: 8, Failures: 4, Errors: 1 +exp_verbose_result=`printf "37%%: Checks: 8, Failures: 4, Errors: 1 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1 @@ -46,22 +51,22 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message" +${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"` else -exp_verbose_result="42%: Checks: 7, Failures: 4, Errors: 0 +exp_verbose_result=`printf "42%%: Checks: 7, Failures: 4, Errors: 0 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message" +${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message"` fi exp_verbose="$suite_output $exp_verbose_result" if [ $HAVE_FORK -eq 1 ]; then -exp_subunit="test: Core:test_pass +exp_subunit=`printf "test: Core:test_pass success: Core:test_pass test: Core:test_fail failure: Core:test_fail [ @@ -83,12 +88,12 @@ test: Core:test_loop failure: Core:test_loop [ ${SRCDIR}ex_output.c:72: Iteration 2 failed ] -test: description \" ' < > &:test_xml_esc_fail_msg -failure: description \" ' < > &:test_xml_esc_fail_msg [ -${SRCDIR}ex_output.c:78: fail \" ' < > & message -]" +test: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg +failure: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg [ +${SRCDIR}ex_output.c:78: fail \" ' < > & \x9 \x0AX\x08 message +]"` else -exp_subunit="test: Core:test_pass +exp_subunit=`printf "test: Core:test_pass success: Core:test_pass test: Core:test_fail failure: Core:test_fail [ @@ -106,17 +111,17 @@ test: Core:test_loop failure: Core:test_loop [ ${SRCDIR}ex_output.c:72: Iteration 2 failed ] -test: description \" ' < > &:test_xml_esc_fail_msg -failure: description \" ' < > &:test_xml_esc_fail_msg [ -${SRCDIR}ex_output.c:78: fail \" ' < > & message -]" +test: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg +failure: description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg [ +${SRCDIR}ex_output.c:78: fail \" ' < > & \x9 \x0AX\x08 message +]"` fi ################## # log output ################## if [ $HAVE_FORK -eq 1 ]; then -expected_log_log="Running suite S1 +expected_log_log=`printf "Running suite S1 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure ${SRCDIR}ex_output.c:46:E:Core:test_exit:0: (after this point) Early exit with return value 1 @@ -125,12 +130,12 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -Running suite XML escape \" ' < > & tests -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message +Running suite XML escape \" ' < > & \x9 \x0AX\x08 tests +${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message Results for all suites run: -37%: Checks: 8, Failures: 4, Errors: 1" +37%%: Checks: 8, Failures: 4, Errors: 1"` else -expected_log_log="Running suite S1 +expected_log_log=`printf "Running suite S1 ${SRCDIR}ex_output.c:31:P:Core:test_pass:0: Passed ${SRCDIR}ex_output.c:37:F:Core:test_fail:0: Failure Running suite S2 @@ -138,10 +143,10 @@ ${SRCDIR}ex_output.c:66:P:Core:test_pass2:0: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:0: Iteration 0 failed ${SRCDIR}ex_output.c:72:P:Core:test_loop:1: Passed ${SRCDIR}ex_output.c:72:F:Core:test_loop:2: Iteration 2 failed -Running suite XML escape \" ' < > & tests -${SRCDIR}ex_output.c:78:F:description \" ' < > &:test_xml_esc_fail_msg:0: fail \" ' < > & message +Running suite XML escape \" ' < > & \x9 \x0AX\x08 tests +${SRCDIR}ex_output.c:78:F:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg:0: fail \" ' < > & \x9 \x0AX\x08 message Results for all suites run: -42%: Checks: 7, Failures: 4, Errors: 0" +42%%: Checks: 7, Failures: 4, Errors: 0"` fi ################## @@ -207,19 +212,19 @@ expected_xml=" - XML escape " ' < > & tests + XML escape " ' < > & X tests ex_output.c:78 test_xml_esc_fail_msg 0 - description " ' < > & - fail " ' < > & message + description " ' < > & X end + fail " ' < > & X message " expected_duration_count=9 else -expected_xml=" +expected_xml=" @@ -271,13 +276,13 @@ expected_xml=" - XML escape " ' < > & tests + XML escape " ' < > & X tests ex_output.c:78 test_xml_esc_fail_msg 0 - description " ' < > & - fail " ' < > & message + description " ' < > & X end + fail " ' < > & X message " @@ -288,16 +293,16 @@ fi # tap output ################## if [ $HAVE_FORK -eq 1 ]; then -expected_normal_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed +expected_normal_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure not ok 3 - ${SRCDIR}ex_output.c:Core:test_exit: Early exit with return value 1 ok 4 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed not ok 5 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Passed not ok 7 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed -not ok 8 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message -1..8" -expected_aborted_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed +not ok 8 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message +1..8"` +expected_aborted_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure not ok 3 - ${SRCDIR}ex_output.c:Core:test_exit: Early exit with return value 1 not ok 4 - ${SRCDIR}ex_output.c:Core:test_abort: Early exit with return value 1 @@ -305,17 +310,17 @@ ok 5 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed not ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed ok 7 - ${SRCDIR}ex_output.c:Core:test_loop: Passed not ok 8 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed -not ok 9 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message -1..9" +not ok 9 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message +1..9"` else -expected_normal_tap="ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed +expected_normal_tap=`printf "ok 1 - ${SRCDIR}ex_output.c:Core:test_pass: Passed not ok 2 - ${SRCDIR}ex_output.c:Core:test_fail: Failure ok 3 - ${SRCDIR}ex_output.c:Core:test_pass2: Passed not ok 4 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 0 failed ok 5 - ${SRCDIR}ex_output.c:Core:test_loop: Passed not ok 6 - ${SRCDIR}ex_output.c:Core:test_loop: Iteration 2 failed -not ok 7 - ${SRCDIR}ex_output.c:description \" ' < > &:test_xml_esc_fail_msg: fail \" ' < > & message -1..7" +not ok 7 - ${SRCDIR}ex_output.c:description \" ' < > & \x9 \x0AX\x08 end:test_xml_esc_fail_msg: fail \" ' < > & \x9 \x0AX\x08 message +1..7"` # When fork() is unavailable, one of the tests # will invoke exit() which will terminate the # unit testing program. In that case, the tap diff --git a/tests/test_xml_output.sh b/tests/test_xml_output.sh index 7497f376..11bb77ad 100755 --- a/tests/test_xml_output.sh +++ b/tests/test_xml_output.sh @@ -25,12 +25,18 @@ if [ x"${expected_duration_count}" != x"${actual_duration_count}" ]; then exit 1; fi -for duration in `grep "\" ${OUTPUT_FILE} | cut -d ">" -f 2 | cut -d "<" -f 1`; do -int_duration=`echo $duration | cut -d "." -f 1` -if [ "${int_duration}" -ne "-1" ] && [ "${int_duration}" -gt "${CK_DEFAULT_TIMEOUT}" ]; then - echo "Problem with duration ${duration}; is not valid. Should be -1 or in [0, ${CK_DEFAULT_TIMEOUT}]" - exit 1 -fi +num_durations=`grep "\" ${OUTPUT_FILE} | wc -l` + +i=1 +while [ ${i} -le ${num_durations} ]; do + duration=`grep "\" ${OUTPUT_FILE} | head -n ${i} | tail -n 1 | cut -d ">" -f 2 | cut -d "<" -f 1` + int_duration=`echo $duration | cut -d "." -f 1` + if [ "${int_duration}" -ne "-1" ] && [ "${int_duration}" -gt "${CK_DEFAULT_TIMEOUT}" ]; then + echo "Problem with duration ${duration}; is not valid. Should be -1 or in [0, ${CK_DEFAULT_TIMEOUT}]" + exit 1 + fi + + i=$((i+1)) done