Commit 164cbdde46d7ec6924782a60f346a6a465a79a26

Authored by Jay Berkenbilt
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.
qpdf/qtest/qpdf.test
... ... @@ -291,11 +291,31 @@ $n_tests += scalar(@bug_tests);
291 291 foreach my $d (@bug_tests)
292 292 {
293 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 320 show_ntests();
301 321 # ----------
... ...
qpdf/qtest/qpdf/issue-118.obfuscated 0 → 100644
  1 +癬踰譯軟霪跛擬笨踰れ鴪匿絃‘頗頗頗┸絃‘┌篤ζ韶縄趁擬縄趁ザ跛※縄趁縄趁れ跛語縄趁頗踰踰汝鯑晋奨凸銘摸摸┸絃‘┌篤晋鹸奨蜑
0 2 \ No newline at end of file
... ...
qpdf/qtest/qpdf/issue-118.pdf deleted
No preview for this file type
qpdf/qtest/qpdf/issue-51.obfuscated 0 → 100644
  1 +癬踰譯軟鞦れ笨鯑晋奨日ζ韶笨笨踰踰笨笨韶笨笨鱆蹊踰鴪踰譯軟鞦笨笨踰踰乍踰譬踰乍糶綟鴪奨踰讓貽踰譬貽踰譬貽踰譬貽踰譬貽踰譬貽踰譬貽踰譬貽踰譬貽絃韶ザ語鴪晋鹸奨蜑
0 2 \ No newline at end of file
... ...
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 8 WARNING: issue-51.pdf (object 2 0, offset 71): unable to recover stream data; treating stream as empty
9 9 WARNING: issue-51.pdf (object 2 0, offset 977): expected endobj
10 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 0 \ No newline at end of file
qpdf/test_driver.cc
... ... @@ -161,6 +161,44 @@ static void print_rect(std::ostream&amp; out,
161 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 202 void runtest(int n, char const* filename1, char const* arg2)
165 203 {
166 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 238 // arg2 is password
201 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 269 else if (n % 2 == 0)
204 270 {
205 271 if (n % 4 == 0)
... ... @@ -217,39 +283,9 @@ void runtest(int n, char const* filename1, char const* arg2)
217 283 else
218 284 {
219 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 291 if ((n == 0) || (n == 1))
... ... @@ -1612,6 +1648,19 @@ void runtest(int n, char const* filename1, char const* arg2)
1612 1648 w.setSuppressOriginalObjectIDs(true);
1613 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 1664 else
1616 1665 {
1617 1666 throw std::runtime_error(std::string("invalid test ") +
... ...