diff --git a/qpdf/qtest/qpdf.test b/qpdf/qtest/qpdf.test index a091456c..1468a438 100644 --- a/qpdf/qtest/qpdf.test +++ b/qpdf/qtest/qpdf.test @@ -291,11 +291,31 @@ $n_tests += scalar(@bug_tests); foreach my $d (@bug_tests) { my ($n, $description, $exit_status) = @$d; - $td->runtest($description, - {$td->COMMAND => "qpdf issue-$n.pdf a.pdf"}, - {$td->FILE => "issue-$n.out", - $td->EXIT_STATUS => $exit_status}, - $td->NORMALIZE_NEWLINES); + if (-f "issue-$n.obfuscated") + { + # Some of the PDF files in the test suite trigger anti-virus + # warnings (MAL/PDFEx-H) and are quarantined or deleted by + # some antivirus software. These files are not actually + # infected files with malicious intent. They are present in + # the test suite to ensure that qpdf does not crash when + # process those files. Base64-encode them and pass them to + # stdin to prevent anti-virus programs from messing up the + # extracted sources. Search for "obfuscated" in test_driver.cc + # for instructions on how to obfuscate input files. + $td->runtest($description, + {$td->COMMAND => "test_driver 45 issue-$n"}, + {$td->FILE => "issue-$n.out", + $td->EXIT_STATUS => $exit_status}, + $td->NORMALIZE_NEWLINES); + } + else + { + $td->runtest($description, + {$td->COMMAND => "qpdf issue-$n.pdf a.pdf"}, + {$td->FILE => "issue-$n.out", + $td->EXIT_STATUS => $exit_status}, + $td->NORMALIZE_NEWLINES); + } } show_ntests(); # ---------- diff --git a/qpdf/qtest/qpdf/issue-118.obfuscated b/qpdf/qtest/qpdf/issue-118.obfuscated new file mode 100644 index 00000000..54ea8fe5 --- /dev/null +++ b/qpdf/qtest/qpdf/issue-118.obfuscated @@ -0,0 +1 @@ +霈Šáýüüüüüüüüüýìü죮ŠÆððÆìì㘵Œ©ìãüüüüüüüüüã‚ìüüüüãüüü¿üìüüüüü〩¢«ž€ìøýôÆòòÆ¿žŸ©­¡Æüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüü¿üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüüüüüüü¿üüüüü¿üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüüüüüüüü©¢š¿žŸ©­¡Æ©¢š£®ŠÆÆüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüü¿üüüüüüüüüüüüüüôìü죮ŠÆððÆìì㘵Œ©ì㔞©ªÆìì㟥¶©ìõÆììㅢš©Žì—üìõ‘Æììã›ì—ýìþìý‘Æìì〩¢«ž€ìÿúÆìì㞣£žìþìüìžÆììãüü¿üìüìüìžÆòòÆ¿žŸ©­¡ÆÌüüüÍÌÃÌÎÌÎüÎüüüÎüüüÎüüüÎüüüÍüüüÍüüüÆ©¢š¿žŸ©­¡Æ©¢š£®ŠÆÆ¿ž­ŸžŽŸ©ªÆúýûÆé鉃Š \ No newline at end of file diff --git a/qpdf/qtest/qpdf/issue-118.pdf b/qpdf/qtest/qpdf/issue-118.pdf deleted file mode 100644 index 5dc05f6d..00000000 Binary files a/qpdf/qtest/qpdf/issue-118.pdf and /dev/null differ diff --git a/qpdf/qtest/qpdf/issue-51.obfuscated b/qpdf/qtest/qpdf/issue-51.obfuscated new file mode 100644 index 00000000..0d81d9ed --- /dev/null +++ b/qpdf/qtest/qpdf/issue-51.obfuscated @@ -0,0 +1 @@ +霈Šáýüüüüüüüüüüüüüþìü죮ŠÆðð〩¢«ž€ìþìüìžãüüüüüüãüüüüüüüüüüüòòÆ¿žŸ©­¡Æüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüýìü죮ŠÆððãüüüüüüüüüüüüüüüüìüìüìžãüüüüüüüüüìüìüìžãüüüüüüüü—üüüüüüüüüüü‘ãüüüüüððãüãüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüòòãüüüüüüüüìþìüìžòòüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüûìü죮ŠÆððãüüüüãüüüüüüüãüüüüüìüìüìžÆãüüüüüüüüüü—ýìüìžìüüüü좹  ì¢¹  ìü‘ÆãüüüüäüüüüüåÆòòüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüüŽŸ©ªÆüìõÆüüüüüüüüüüìüüüüüìªìÆüüüüüüüþüüìüüüüüì¢ìÆüüüüüüüüüõìüüüüüì¢ìÆüüüüüüüüüüìüüüüüì¢ìÆüüüüüüüüüüìüüüüüì¢ìÆüüüüüüüüüüìüüüüüì¢ìÆüüüüüüüüüüìüüüüüì¢ìÆüüüüüüüøüüìüüüüüì¢ìÆüüüüüüüüüüìüüüüüì¢ìÆžŸ­¥ ©Ÿðð㟥¶©ìü㞣£žìûìüìžòò¿ž­ŸžŽŸ©ªÆûøüÆé鉃Š \ No newline at end of file diff --git a/qpdf/qtest/qpdf/issue-51.out b/qpdf/qtest/qpdf/issue-51.out index 692c0984..e361b7d0 100644 --- a/qpdf/qtest/qpdf/issue-51.out +++ b/qpdf/qtest/qpdf/issue-51.out @@ -8,4 +8,3 @@ WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream leng WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj -qpdf: operation succeeded with warnings; resulting file may have some problems diff --git a/qpdf/qtest/qpdf/issue-51.pdf b/qpdf/qtest/qpdf/issue-51.pdf deleted file mode 100644 index 2dafce1a..00000000 --- a/qpdf/qtest/qpdf/issue-51.pdf +++ /dev/null @@ -1,22 +0,0 @@ -%PDF-100000000000002 0 obj -<> -stream -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 0 obj -<>/00000000 2 0 R>>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 0 obj -<>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000xref -0 9 -0000000000 00000 f -0000000200 00000 n -0000000009 00000 n -0000000000 00000 n -0000000000 00000 n -0000000000 00000 n -0000000000 00000 n -0000000400 00000 n -0000000000 00000 n -trailer<>startxref -740 -%%EOF \ No newline at end of file diff --git a/qpdf/test_driver.cc b/qpdf/test_driver.cc index 14a317ba..69d13d54 100644 --- a/qpdf/test_driver.cc +++ b/qpdf/test_driver.cc @@ -161,6 +161,44 @@ static void print_rect(std::ostream& out, << r.urx << ", " << r.ury << "]"; } +static void read_file_into_memory( + char const* filename, + PointerHolder& file_buf, size_t& size) +{ + FILE* f = QUtil::safe_fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + size = QUtil::tell(f); + fseek(f, 0, SEEK_SET); + file_buf = PointerHolder(true, new char[size]); + char* buf_p = file_buf.getPointer(); + size_t bytes_read = 0; + size_t len = 0; + while ((len = fread(buf_p + bytes_read, 1, size - bytes_read, f)) > 0) + { + bytes_read += len; + } + if (bytes_read != size) + { + if (ferror(f)) + { + throw std::runtime_error( + std::string("failure reading file ") + filename + + " into memory: read " + + QUtil::int_to_string(bytes_read) + "; wanted " + + QUtil::int_to_string(size)); + } + else + { + throw std::logic_error( + std::string("premature eof reading file ") + filename + + " into memory: read " + + QUtil::int_to_string(bytes_read) + "; wanted " + + QUtil::int_to_string(size)); + } + } + fclose(f); +} + void runtest(int n, char const* filename1, char const* arg2) { // Most tests here are crafted to work on specific files. Look at @@ -200,6 +238,34 @@ void runtest(int n, char const* filename1, char const* arg2) // arg2 is password pdf.processFile(filename1, arg2); } + else if (n == 45) + { + // Decode obfuscated files. To obfuscated, run the input file + // through this perl script, and save the result to + // filename.obfuscated. This pretends that the input was + // called filename.pdf and that that file contained the + // deobfuscated version. + + // undef $/; + // my @str = split('', ); + // for (my $i = 0; $i < scalar(@str); ++$i) + // { + // $str[$i] = chr(ord($str[$i]) ^ 0xcc); + // } + // print(join('', @str)); + + std::string filename(std::string(filename1) + ".obfuscated"); + PointerHolder file_buf; + size_t size = 0; + read_file_into_memory(filename.c_str(), file_buf, size); + char* p = file_buf.getPointer(); + for (size_t i = 0; i < size; ++i) + { + p[i] ^= 0xcc; + } + pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(), + p, size); + } else if (n % 2 == 0) { if (n % 4 == 0) @@ -217,39 +283,9 @@ void runtest(int n, char const* filename1, char const* arg2) else { QTC::TC("qpdf", "exercise processMemoryFile"); - FILE* f = QUtil::safe_fopen(filename1, "rb"); - fseek(f, 0, SEEK_END); - size_t size = QUtil::tell(f); - fseek(f, 0, SEEK_SET); - file_buf = PointerHolder(true, new char[size]); - char* buf_p = file_buf.getPointer(); - size_t bytes_read = 0; - size_t len = 0; - while ((len = fread(buf_p + bytes_read, 1, size - bytes_read, f)) > 0) - { - bytes_read += len; - } - if (bytes_read != size) - { - if (ferror(f)) - { - throw std::runtime_error( - std::string("failure reading file ") + filename1 + - " into memory: read " + - QUtil::int_to_string(bytes_read) + "; wanted " + - QUtil::int_to_string(size)); - } - else - { - throw std::logic_error( - std::string("premature eof reading file ") + filename1 + - " into memory: read " + - QUtil::int_to_string(bytes_read) + "; wanted " + - QUtil::int_to_string(size)); - } - } - fclose(f); - pdf.processMemoryFile(filename1, buf_p, size); + size_t size = 0; + read_file_into_memory(filename1, file_buf, size); + pdf.processMemoryFile(filename1, file_buf.getPointer(), size); } if ((n == 0) || (n == 1)) @@ -1612,6 +1648,19 @@ void runtest(int n, char const* filename1, char const* arg2) w.setSuppressOriginalObjectIDs(true); w.write(); } + else if (n == 45) + { + // Decode obfuscated files. This is here to help test with + // files that trigger anti-virus warnings. See comments in + // qpdf.test for details. + QPDFWriter w(pdf, "a.pdf"); + w.setStaticID(true); + w.write(); + if (! pdf.getWarnings().empty()) + { + exit(3); + } + } else { throw std::runtime_error(std::string("invalid test ") +