class

Mica::Testing::ByteMutator

Inherits Reference < Object

Seeded, replayable byte-level mutations for fuzzing any format.

Every case is a pure function of (seed, iteration), so a failure can be replayed exactly with case_bytes. Mutations range from structural (insert a format token, delete, duplicate, splice, truncate) to hostile bytes: every class of invalid UTF-8, forbidden controls, noncharacters, byte order marks in the wrong place, and re-encoding the whole input (or only its tail) as UTF-16, UTF-32 or ISO-8859-1.

A format supplies its seed documents and grammar tokens:

mutator = Mica::Testing::ByteMutator.new(
  list_of_seeds: ["<a/>".to_slice],
  list_of_tokens: ["<", "&amp;"].map(&.to_slice),
)
mutator.case_bytes(7_i64, 42) # => Bytes[...]

Constants

HOSTILE = {"overlong" => [Bytes[192, 128], Bytes[193, 191], Bytes[224, 128, 128], Bytes[224, 159, 191], Bytes[240, 128, 128, 128], Bytes[240, 143, 191, 191]], "surrogate" => [Bytes[237, 160, 128], Bytes[237, 191, 191], Bytes[237, 175, 191, 237, 176, 128]], "above-10ffff" => [Bytes[244, 144, 128, 128], Bytes[245, 128, 128, 128], Bytes[247, 191, 191, 191]], "five-six-byte" => [Bytes[248, 136, 128, 128, 128], Bytes[251, 191, 191, 191, 191], Bytes[252, 132, 128, 128, 128, 128], Bytes[253, 191, 191, 191, 191, 191]], "fe-ff" => [Bytes[254], Bytes[255], Bytes[254, 255], Bytes[255, 254]], "lone-continuation" => [Bytes[128], Bytes[191], Bytes[128, 128, 128]], "truncated" => [Bytes[195], Bytes[226, 130], Bytes[240, 159, 152], Bytes[226]], "nul" => [Bytes[0], Bytes[0, 0]], "c0-control" => (1..31).reject do |code| {9, 10, 13}.includes?(code) end.map do |code| Bytes[code.to_u8] end, "del" => [Bytes[127]], "c1-control" => [Bytes[194, 128], Bytes[194, 132], Bytes[194, 134], Bytes[194, 159]], "line-end" => [Bytes[13], Bytes[13, 10], Bytes[194, 133], Bytes[13, 194, 133], Bytes[226, 128, 168], Bytes[13, 226, 128, 168]], "noncharacter" => [Bytes[239, 191, 190], Bytes[239, 191, 191], Bytes[244, 143, 191, 191], Bytes[239, 183, 144]], "byte-order-mark" => [Bytes[239, 187, 191], Bytes[254, 255], Bytes[255, 254], Bytes[0, 0, 254, 255], Bytes[255, 254, 0, 0]], "latin1-in-utf8" => [Bytes[233], Bytes[99, 97, 102, 233], Bytes[160]], "combining-character" => [Bytes[204, 129], Bytes[225, 133, 161]]}

The hostile byte classes, each with sample sequences.

HOSTILE_SEQUENCES = HOSTILE.values.flatten

The flattened list of hostile sequences.

MAX_MUTATIONS = 64

Most mutations one case may receive.

Constructors

new(list_of_seeds : Array(Bytes), list_of_tokens : Array(Bytes) = [] of Bytes, max_mutations : Int32 = 4)
Source

Instance methods

case_bytes(seed : Int64, iteration : Int32) : Bytes

Returns the bytes for case iteration under seed.

Source
insert(bytes : Bytes, position : Int32, extra : Bytes) : Bytes

Returns bytes with extra inserted at position.

Source
latin1(bytes : Bytes) : Bytes

Encodes bytes as ISO-8859-1: code points up to U+00FF become one byte, others become ?.

Source
list_of_seeds

Returns the seed inputs.

Source
list_of_tokens

Returns the grammar tokens.

Source
mutate(bytes : Bytes, random : Random) : Bytes

Applies one random mutation.

Source
reencode(bytes : Bytes, random : Random) : Bytes

Re-encodes the input, or only its tail (mixed encodings), as UTF-16, UTF-32 or ISO-8859-1, with or without a byte order mark.

Source
remove(bytes : Bytes, start : Int32, length : Int32) : Bytes

Returns bytes without length bytes from start.

Source
utf16(bytes : Bytes, big_endian : Bool, with_mark : Bool = true, lone_surrogate : Bool = false) : Bytes

Encodes the valid UTF-8 in bytes as UTF-16; each invalid sequence becomes a lone low surrogate. With lone_surrogate one extra high surrogate is appended without its pair.

Source
utf32(bytes : Bytes, big_endian : Bool, with_mark : Bool = true) : Bytes

Encodes the valid UTF-8 in bytes as UTF-32; each invalid sequence becomes the out-of-range value 0x110000.

Source