def test_parse_args(capsys, tmp_path): "test that grizzly.reduce args are accepted and validated" exe = tmp_path / "binary" inp = tmp_path / "input" # missing arg tests with pytest.raises(SystemExit): ReducerArgs().parse_args([]) _, err = capsys.readouterr() with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe)]) _, err = capsys.readouterr() # invalid binary tests with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp)]) _, err = capsys.readouterr() assert "error: file not found: %r" % (str(exe),) in err exe.mkdir() with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp)]) _, err = capsys.readouterr() assert "error: file not found: %r" % (str(exe),) in err exe.rmdir() exe.touch() # invalid input tests with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp)]) _, err = capsys.readouterr() assert "error: %r does not exist" % (str(inp),) in err inp.touch() with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp)]) _, err = capsys.readouterr() assert "error: Testcase should be a folder, zip, or html file" in err inp.unlink() with pytest.raises(SystemExit): ReducerFuzzManagerIDArgs().parse_args([str(exe), str(inp)]) _, err = capsys.readouterr() assert "invalid int value" in err # valid binary & inputs (tmp_path / "input.zip").touch() zipf = tmp_path / "input.zip" ReducerArgs().parse_args([str(exe), str(zipf)]) zipf.unlink() inp.mkdir() (inp / "test_info.txt").touch() ReducerArgs().parse_args([str(exe), str(inp)]) ReducerFuzzManagerIDArgs().parse_args([str(exe), '123']) # sig/environ tests fname = tmp_path / "file.txt" for arg in ("--sig", "--environ"): with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp), arg, str(fname)]) _, err = capsys.readouterr() assert "error: file not found: %r" % (str(fname),) in err fname.mkdir() with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp), arg, str(fname)]) _, err = capsys.readouterr() assert "error: file not found: %r" % (str(fname),) in err fname.rmdir() fname.touch() ReducerArgs().parse_args([str(exe), str(inp), arg, str(fname)]) fname.unlink() # repeat/min-crashes tests for arg in ("--repeat", "--min-crashes"): with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp), arg, "abc"]) with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp), arg, "-1"]) _, err = capsys.readouterr() assert "'%s' value must be positive" % (arg,) in err with pytest.raises(SystemExit): ReducerArgs().parse_args([str(exe), str(inp), arg, "0"]) _, err = capsys.readouterr() assert "'%s' value must be positive" % (arg,) in err ReducerArgs().parse_args([str(exe), str(inp), arg, "1"]) ReducerArgs().parse_args([str(exe), str(inp), arg, "10"])
def test_crash_main_no_repro_specific(job, monkeypatch, tmp_path): # noqa pylint: disable=redefined-outer-name "crash.main --fuzzmanager updates quality" expect_patch = [reporter.FuzzManagerReporter.QUAL_NOT_REPRODUCIBLE] class ReporterNoSubmit(reporter.FuzzManagerReporter): (tmp_path / ".fuzzmanagerconf").touch() FM_CONFIG = str(tmp_path / ".fuzzmanagerconf") def _reset(self): pass def _submit(self, *_args, **_kwds): # make sure _submit() is not called assert False class FakeCollector(object): serverProtocol = 'https' serverHost = 'mozilla.org' serverPort = 8000 def get(self, _url, **kwds): class response(object): class headers(object): @staticmethod def get(value, default): assert value.lower() == 'content-disposition' return 'attachment; filename="test.zip"' @staticmethod def json(): return { 'testcase_quality': reporter.FuzzManagerReporter.QUAL_REQUEST_SPECIFIC, 'tool': 'test-tool'} content = (inp / "test.zip").read_bytes() return response def patch(self, _url, **kwds): data = kwds["data"] assert set(data.keys()) == {"testcase_quality"} assert expect_patch assert data["testcase_quality"] == expect_patch.pop(0) # uses the job fixture from test_reduce which reduces testcases to the string "required\n" monkeypatch.setattr(reduce, "ReductionJob", lambda *a, **kw: job) monkeypatch.setattr(reporter, "Collector", FakeCollector) monkeypatch.setattr(reduce, "FuzzManagerReporter", ReporterNoSubmit) monkeypatch.setattr(crash, "Collector", FakeCollector) (tmp_path / "binary").touch() exe = tmp_path / "binary" (tmp_path / "binary.fuzzmanagerconf").write_text( "[Main]\n" "platform = x86-64\n" "product = mozilla-central\n" "os = linux\n" ) (tmp_path / "input").mkdir() inp = tmp_path / "input" (inp / "test_info.txt").write_text("landing page: test.html") (inp / "test.html").write_text("fluff\n") with zipfile.ZipFile(str(inp / "test.zip"), "w") as zip_fp: zip_fp.write(str(inp / "test_info.txt"), "test_info.txt") zip_fp.write(str(inp / "test.html"), "test.html") args = ReducerFuzzManagerIDArgs().parse_args([str(exe), '1234', '--fuzzmanager']) assert crash.main(args) == 1 assert not expect_patch
def test_crash_main_repro(monkeypatch, tmp_path): # noqa pylint: disable=redefined-outer-name "crash.main --fuzzmanager updates quality" # expect Collector.patch to be called with these qualities expect_patch = [ reporter.FuzzManagerReporter.QUAL_REPRODUCIBLE, reporter.FuzzManagerReporter.QUAL_REDUCED_ORIGINAL ] submitted = [False] class ReporterNoSubmit(reporter.FuzzManagerReporter): (tmp_path / ".fuzzmanagerconf").touch() FM_CONFIG = str(tmp_path / ".fuzzmanagerconf") def _submit_report(self, *_args, **_kwds): # check that the crash was already marked reproducible, but not yet marked reduced assert expect_patch == [ reporter.FuzzManagerReporter.QUAL_REDUCED_ORIGINAL ] submitted[0] = True class FakeCollector(object): serverProtocol = 'https' serverHost = 'mozilla.org' serverPort = 8000 def get(self, _url, **kwds): class response(object): class headers(object): @staticmethod def get(value, default): assert value.lower() == 'content-disposition' return 'attachment; filename="test.zip"' @staticmethod def json(): return { 'testcase_quality': reporter.FuzzManagerReporter.QUAL_UNREDUCED, 'tool': 'test-tool' } content = (inp / "test.zip").read_bytes() return response def patch(self, _url, **kwds): data = kwds["data"] assert set(data.keys()) == {"testcase_quality"} assert expect_patch assert data["testcase_quality"] == expect_patch.pop(0) monkeypatch.setattr(reporter, "Collector", FakeCollector) monkeypatch.setattr(reduce, "FuzzManagerReporter", ReporterNoSubmit) monkeypatch.setattr(crash, "Collector", FakeCollector) (tmp_path / "binary").touch() exe = tmp_path / "binary" (tmp_path / "binary.fuzzmanagerconf").write_text( "[Main]\n" "platform = x86-64\n" "product = mozilla-central\n" "os = linux\n") (tmp_path / "input").mkdir() inp = tmp_path / "input" (inp / "test_info.txt").write_text("landing page: test.html") (inp / "test.html").write_text("fluff\nrequired\n") with zipfile.ZipFile(str(inp / "test.zip"), "w") as zip_fp: zip_fp.write(str(inp / "test_info.txt"), "test_info.txt") zip_fp.write(str(inp / "test.html"), "test.html") args = ReducerFuzzManagerIDArgs().parse_args( [str(exe), '1234', '--fuzzmanager']) assert TestMainCrashReductionJob.main(args) == 0 assert not expect_patch assert submitted[0]