Commit 164cbdde46d7ec6924782a60f346a6a465a79a26
1 parent
5d9d80be
Protect against virus warnings (fixes #216)
Some files in the test suite trigger antivirus warnings. These are not infected files with malicious intent. They are test files to ensure that qpdf does not crash when it encounters the files. This change enables those files to be obfuscated in the source repository so that checking out qpdf from version control or extracting the source code doesn't trigger antivirus warnings.
Showing
7 changed files
with
109 additions
and
61 deletions
qpdf/qtest/qpdf.test
| @@ -291,11 +291,31 @@ $n_tests += scalar(@bug_tests); | @@ -291,11 +291,31 @@ $n_tests += scalar(@bug_tests); | ||
| 291 | foreach my $d (@bug_tests) | 291 | foreach my $d (@bug_tests) |
| 292 | { | 292 | { |
| 293 | my ($n, $description, $exit_status) = @$d; | 293 | my ($n, $description, $exit_status) = @$d; |
| 294 | - $td->runtest($description, | ||
| 295 | - {$td->COMMAND => "qpdf issue-$n.pdf a.pdf"}, | ||
| 296 | - {$td->FILE => "issue-$n.out", | ||
| 297 | - $td->EXIT_STATUS => $exit_status}, | ||
| 298 | - $td->NORMALIZE_NEWLINES); | 294 | + if (-f "issue-$n.obfuscated") |
| 295 | + { | ||
| 296 | + # Some of the PDF files in the test suite trigger anti-virus | ||
| 297 | + # warnings (MAL/PDFEx-H) and are quarantined or deleted by | ||
| 298 | + # some antivirus software. These files are not actually | ||
| 299 | + # infected files with malicious intent. They are present in | ||
| 300 | + # the test suite to ensure that qpdf does not crash when | ||
| 301 | + # process those files. Base64-encode them and pass them to | ||
| 302 | + # stdin to prevent anti-virus programs from messing up the | ||
| 303 | + # extracted sources. Search for "obfuscated" in test_driver.cc | ||
| 304 | + # for instructions on how to obfuscate input files. | ||
| 305 | + $td->runtest($description, | ||
| 306 | + {$td->COMMAND => "test_driver 45 issue-$n"}, | ||
| 307 | + {$td->FILE => "issue-$n.out", | ||
| 308 | + $td->EXIT_STATUS => $exit_status}, | ||
| 309 | + $td->NORMALIZE_NEWLINES); | ||
| 310 | + } | ||
| 311 | + else | ||
| 312 | + { | ||
| 313 | + $td->runtest($description, | ||
| 314 | + {$td->COMMAND => "qpdf issue-$n.pdf a.pdf"}, | ||
| 315 | + {$td->FILE => "issue-$n.out", | ||
| 316 | + $td->EXIT_STATUS => $exit_status}, | ||
| 317 | + $td->NORMALIZE_NEWLINES); | ||
| 318 | + } | ||
| 299 | } | 319 | } |
| 300 | show_ntests(); | 320 | show_ntests(); |
| 301 | # ---------- | 321 | # ---------- |
qpdf/qtest/qpdf/issue-118.obfuscated
0 → 100644
qpdf/qtest/qpdf/issue-118.pdf deleted
No preview for this file type
qpdf/qtest/qpdf/issue-51.obfuscated
0 → 100644
qpdf/qtest/qpdf/issue-51.out
| @@ -8,4 +8,3 @@ WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream leng | @@ -8,4 +8,3 @@ WARNING: issue-51.pdf (object 2 0, offset 71): attempting to recover stream leng | ||
| 8 | WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty | 8 | WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty |
| 9 | WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj | 9 | WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj |
| 10 | WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj | 10 | WARNING: issue-51.pdf (object 2 0, offset 977): EOF after endobj |
| 11 | -qpdf: operation succeeded with warnings; resulting file may have some problems |
qpdf/qtest/qpdf/issue-51.pdf deleted
| 1 | -%PDF-100000000000002 0 obj | ||
| 2 | -<</Length 2 0 R/000000/00000000000>> | ||
| 3 | -stream | ||
| 4 | -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 0 obj | ||
| 5 | -<</0000000000000000 0 0 R/000000000 0 0 R/00000000[00000000000]/00000<</0/00000000000000000000000000000000>>/00000000 2 0 R>>000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007 0 obj | ||
| 6 | -<</0000/0000000/00000 0 0 R | ||
| 7 | -/0000000000[1 0 R 0000 null null 0] | ||
| 8 | -/0000(00000) | ||
| 9 | ->>0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000xref | ||
| 10 | -0 9 | ||
| 11 | -0000000000 00000 f | ||
| 12 | -0000000200 00000 n | ||
| 13 | -0000000009 00000 n | ||
| 14 | -0000000000 00000 n | ||
| 15 | -0000000000 00000 n | ||
| 16 | -0000000000 00000 n | ||
| 17 | -0000000000 00000 n | ||
| 18 | -0000000400 00000 n | ||
| 19 | -0000000000 00000 n | ||
| 20 | -trailer<</Size 0/Root 7 0 R>>startxref | ||
| 21 | -740 | ||
| 22 | -%%EOF | ||
| 23 | \ No newline at end of file | 0 | \ No newline at end of file |
qpdf/test_driver.cc
| @@ -161,6 +161,44 @@ static void print_rect(std::ostream& out, | @@ -161,6 +161,44 @@ static void print_rect(std::ostream& out, | ||
| 161 | << r.urx << ", " << r.ury << "]"; | 161 | << r.urx << ", " << r.ury << "]"; |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | +static void read_file_into_memory( | ||
| 165 | + char const* filename, | ||
| 166 | + PointerHolder<char>& file_buf, size_t& size) | ||
| 167 | +{ | ||
| 168 | + FILE* f = QUtil::safe_fopen(filename, "rb"); | ||
| 169 | + fseek(f, 0, SEEK_END); | ||
| 170 | + size = QUtil::tell(f); | ||
| 171 | + fseek(f, 0, SEEK_SET); | ||
| 172 | + file_buf = PointerHolder<char>(true, new char[size]); | ||
| 173 | + char* buf_p = file_buf.getPointer(); | ||
| 174 | + size_t bytes_read = 0; | ||
| 175 | + size_t len = 0; | ||
| 176 | + while ((len = fread(buf_p + bytes_read, 1, size - bytes_read, f)) > 0) | ||
| 177 | + { | ||
| 178 | + bytes_read += len; | ||
| 179 | + } | ||
| 180 | + if (bytes_read != size) | ||
| 181 | + { | ||
| 182 | + if (ferror(f)) | ||
| 183 | + { | ||
| 184 | + throw std::runtime_error( | ||
| 185 | + std::string("failure reading file ") + filename + | ||
| 186 | + " into memory: read " + | ||
| 187 | + QUtil::int_to_string(bytes_read) + "; wanted " + | ||
| 188 | + QUtil::int_to_string(size)); | ||
| 189 | + } | ||
| 190 | + else | ||
| 191 | + { | ||
| 192 | + throw std::logic_error( | ||
| 193 | + std::string("premature eof reading file ") + filename + | ||
| 194 | + " into memory: read " + | ||
| 195 | + QUtil::int_to_string(bytes_read) + "; wanted " + | ||
| 196 | + QUtil::int_to_string(size)); | ||
| 197 | + } | ||
| 198 | + } | ||
| 199 | + fclose(f); | ||
| 200 | +} | ||
| 201 | + | ||
| 164 | void runtest(int n, char const* filename1, char const* arg2) | 202 | void runtest(int n, char const* filename1, char const* arg2) |
| 165 | { | 203 | { |
| 166 | // Most tests here are crafted to work on specific files. Look at | 204 | // 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) | @@ -200,6 +238,34 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 200 | // arg2 is password | 238 | // arg2 is password |
| 201 | pdf.processFile(filename1, arg2); | 239 | pdf.processFile(filename1, arg2); |
| 202 | } | 240 | } |
| 241 | + else if (n == 45) | ||
| 242 | + { | ||
| 243 | + // Decode obfuscated files. To obfuscated, run the input file | ||
| 244 | + // through this perl script, and save the result to | ||
| 245 | + // filename.obfuscated. This pretends that the input was | ||
| 246 | + // called filename.pdf and that that file contained the | ||
| 247 | + // deobfuscated version. | ||
| 248 | + | ||
| 249 | + // undef $/; | ||
| 250 | + // my @str = split('', <STDIN>); | ||
| 251 | + // for (my $i = 0; $i < scalar(@str); ++$i) | ||
| 252 | + // { | ||
| 253 | + // $str[$i] = chr(ord($str[$i]) ^ 0xcc); | ||
| 254 | + // } | ||
| 255 | + // print(join('', @str)); | ||
| 256 | + | ||
| 257 | + std::string filename(std::string(filename1) + ".obfuscated"); | ||
| 258 | + PointerHolder<char> file_buf; | ||
| 259 | + size_t size = 0; | ||
| 260 | + read_file_into_memory(filename.c_str(), file_buf, size); | ||
| 261 | + char* p = file_buf.getPointer(); | ||
| 262 | + for (size_t i = 0; i < size; ++i) | ||
| 263 | + { | ||
| 264 | + p[i] ^= 0xcc; | ||
| 265 | + } | ||
| 266 | + pdf.processMemoryFile((std::string(filename1) + ".pdf").c_str(), | ||
| 267 | + p, size); | ||
| 268 | + } | ||
| 203 | else if (n % 2 == 0) | 269 | else if (n % 2 == 0) |
| 204 | { | 270 | { |
| 205 | if (n % 4 == 0) | 271 | if (n % 4 == 0) |
| @@ -217,39 +283,9 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -217,39 +283,9 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 217 | else | 283 | else |
| 218 | { | 284 | { |
| 219 | QTC::TC("qpdf", "exercise processMemoryFile"); | 285 | QTC::TC("qpdf", "exercise processMemoryFile"); |
| 220 | - FILE* f = QUtil::safe_fopen(filename1, "rb"); | ||
| 221 | - fseek(f, 0, SEEK_END); | ||
| 222 | - size_t size = QUtil::tell(f); | ||
| 223 | - fseek(f, 0, SEEK_SET); | ||
| 224 | - file_buf = PointerHolder<char>(true, new char[size]); | ||
| 225 | - char* buf_p = file_buf.getPointer(); | ||
| 226 | - size_t bytes_read = 0; | ||
| 227 | - size_t len = 0; | ||
| 228 | - while ((len = fread(buf_p + bytes_read, 1, size - bytes_read, f)) > 0) | ||
| 229 | - { | ||
| 230 | - bytes_read += len; | ||
| 231 | - } | ||
| 232 | - if (bytes_read != size) | ||
| 233 | - { | ||
| 234 | - if (ferror(f)) | ||
| 235 | - { | ||
| 236 | - throw std::runtime_error( | ||
| 237 | - std::string("failure reading file ") + filename1 + | ||
| 238 | - " into memory: read " + | ||
| 239 | - QUtil::int_to_string(bytes_read) + "; wanted " + | ||
| 240 | - QUtil::int_to_string(size)); | ||
| 241 | - } | ||
| 242 | - else | ||
| 243 | - { | ||
| 244 | - throw std::logic_error( | ||
| 245 | - std::string("premature eof reading file ") + filename1 + | ||
| 246 | - " into memory: read " + | ||
| 247 | - QUtil::int_to_string(bytes_read) + "; wanted " + | ||
| 248 | - QUtil::int_to_string(size)); | ||
| 249 | - } | ||
| 250 | - } | ||
| 251 | - fclose(f); | ||
| 252 | - pdf.processMemoryFile(filename1, buf_p, size); | 286 | + size_t size = 0; |
| 287 | + read_file_into_memory(filename1, file_buf, size); | ||
| 288 | + pdf.processMemoryFile(filename1, file_buf.getPointer(), size); | ||
| 253 | } | 289 | } |
| 254 | 290 | ||
| 255 | if ((n == 0) || (n == 1)) | 291 | if ((n == 0) || (n == 1)) |
| @@ -1612,6 +1648,19 @@ void runtest(int n, char const* filename1, char const* arg2) | @@ -1612,6 +1648,19 @@ void runtest(int n, char const* filename1, char const* arg2) | ||
| 1612 | w.setSuppressOriginalObjectIDs(true); | 1648 | w.setSuppressOriginalObjectIDs(true); |
| 1613 | w.write(); | 1649 | w.write(); |
| 1614 | } | 1650 | } |
| 1651 | + else if (n == 45) | ||
| 1652 | + { | ||
| 1653 | + // Decode obfuscated files. This is here to help test with | ||
| 1654 | + // files that trigger anti-virus warnings. See comments in | ||
| 1655 | + // qpdf.test for details. | ||
| 1656 | + QPDFWriter w(pdf, "a.pdf"); | ||
| 1657 | + w.setStaticID(true); | ||
| 1658 | + w.write(); | ||
| 1659 | + if (! pdf.getWarnings().empty()) | ||
| 1660 | + { | ||
| 1661 | + exit(3); | ||
| 1662 | + } | ||
| 1663 | + } | ||
| 1615 | else | 1664 | else |
| 1616 | { | 1665 | { |
| 1617 | throw std::runtime_error(std::string("invalid test ") + | 1666 | throw std::runtime_error(std::string("invalid test ") + |