blob: fcad345e857ef7470de9184ba6c82d2eb5f53148 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
// Author: cirakg & claude ai
// Summary: Runs the seeded fuzz corpus through the tokenizer, the Aegis reader and the importer: always a result or a typed error, never a crash.
//
// README 17.3. Each input must end in a definite state within a bounded
// number of steps (no endless loop), and anything the importer stores must
// be a valid entry. Watchdog limits aren't enforced in unit tests; the
// per-step bounds are covered by the UI flow on the Forerunner 255S.
import Toybox.Lang;
import Toybox.Test;
(:test)
function fuzzTokenizerAlwaysTerminates(logger as Test.Logger) as Boolean {
var inputs = FuzzCorpus.JSON;
for (var i = 0; i < inputs.size(); i++) {
var data = Bytes.fromHex(inputs[i] as String);
var tok = new Json.Tokenizer(data);
var finished = false;
// Every token consumes at least one byte, so size + 2 calls suffice.
for (var n = 0; n < data.size() + 2; n++) {
var t = tok.next();
if (t == Json.T_END || t == Json.T_INCOMPLETE || t == Json.T_ERROR) {
finished = true;
break;
}
}
if (!finished) {
logger.error("tokenizer didn't finish on input " + i);
return false;
}
}
return true;
}
(:test)
function fuzzAegisReaderGivesDefiniteResults(logger as Test.Logger) as Boolean {
var inputs = FuzzCorpus.JSON;
var done = 0;
for (var i = 0; i < inputs.size(); i++) {
var data = Bytes.fromHex(inputs[i] as String);
var reader = new AegisReader(data);
var status = AegisReader.RUNNING;
for (var n = 0; n < data.size() + 2 && status == AegisReader.RUNNING; n++) {
status = reader.step();
}
if (status == AegisReader.RUNNING || (status == AegisReader.FAILED && reader.error == null)) {
logger.error("input " + i + ": status " + status);
return false;
}
if (status == AegisReader.DONE) {
done++;
for (var k = 0; k < reader.items.size(); k++) {
var item = reader.item(k);
var e = item.entry;
if (e == null ? item.problem == null && item.skipped == null : e.validate() != null) {
logger.error("input " + i + " item " + k + " is neither valid nor explained");
return false;
}
}
}
}
logger.debug("FUZZ aegis inputs=" + inputs.size() + " complete=" + done);
return done > 0;
}
// The whole importer on every input that is valid text: a report that
// explains everything, and only valid entries in the vault.
(:test)
function fuzzImporterNeverStoresInvalidEntries(logger as Test.Logger) as Boolean {
var all = [] as Array<String>;
all.addAll(FuzzCorpus.JSON as Array<String>);
all.addAll(FuzzCorpus.URIS as Array<String>);
var imported = 0;
for (var i = 0; i < all.size(); i++) {
var text = Bytes.toUtf8(Bytes.fromHex(all[i]));
if (text == null) {
continue;
}
var vault = new Vault(new MemoryBackend(), new FakeTime());
var job = new Importer(vault, text, Importer.MODE_APPEND, false, 1800000000l);
var finished = false;
for (var n = 0; n < 5000; n++) {
if (job.step(1)) {
finished = true;
break;
}
}
var r = job.report;
if (!finished || (r.failure == null && r.waitingChars < 0 && r.summary().find("Imported") != 0)) {
logger.error("input " + i + " didn't finish cleanly: " + r.summary());
return false;
}
var ids = vault.ids();
for (var k = 0; k < ids.size(); k++) {
var e = vault.open(ids[k]);
var problem = e.validate();
e.wipeSecrets();
if (problem != null) {
logger.error("input " + i + " stored an invalid entry: " + problem);
return false;
}
}
imported += ids.size();
}
logger.debug("FUZZ importer inputs=" + all.size() + " entries_stored=" + imported);
return imported > 0;
}
|