tests.nct
see ./index.nct
use std/io.BlockingReader
use std/vec.Vec
struct FragmentedSource {
bytes: Vec<u8>
next: usize
}
instance FragmentedSource {
blocking method &+self.read_blocking(output: &+[u8]): usize! {
if output.len() == 0 || self.next == self.bytes.len() { return 0 }
output[0] = self.bytes[self.next]
self.next += 1
return 1
}
}
func valid_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(237),
u8.truncate(205),
u8.truncate(49),
u8.truncate(10),
u8.truncate(132),
u8.truncate(64),
u8.truncate(16),
u8.truncate(4),
u8.truncate(192),
u8.truncate(121),
u8.truncate(138),
u8.truncate(47),
u8.truncate(56),
u8.truncate(68),
u8.truncate(68),
u8.truncate(223),
u8.truncate(35),
u8.truncate(122),
u8.truncate(169),
u8.truncate(194),
u8.truncate(185),
u8.truncate(130),
u8.truncate(207),
u8.truncate(119),
u8.truncate(53),
u8.truncate(146),
u8.truncate(203),
u8.truncate(21),
u8.truncate(196),
u8.truncate(170),
u8.truncate(164),
u8.truncate(135),
u8.truncate(238),
u8.truncate(96),
u8.truncate(186),
u8.truncate(79),
u8.truncate(90),
u8.truncate(83),
u8.truncate(92),
u8.truncate(171),
u8.truncate(204),
u8.truncate(154),
u8.truncate(186),
u8.truncate(62),
u8.truncate(50),
u8.truncate(251),
u8.truncate(207),
u8.truncate(125),
u8.truncate(61),
u8.truncate(221),
u8.truncate(123),
u8.truncate(223),
u8.truncate(180),
u8.truncate(85),
u8.truncate(21),
u8.truncate(69),
u8.truncate(25),
u8.truncate(55),
u8.truncate(88),
u8.truncate(230),
u8.truncate(212),
u8.truncate(253),
u8.truncate(242),
u8.truncate(203),
u8.truncate(120),
u8.truncate(167),
u8.truncate(249),
u8.truncate(219),
u8.truncate(79),
u8.truncate(227),
u8.truncate(16),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(60),
u8.truncate(204),
u8.truncate(6),
u8.truncate(189),
u8.truncate(244),
u8.truncate(210),
u8.truncate(47),
u8.truncate(0),
u8.truncate(40),
u8.truncate(0),
u8.truncate(0),
]
}
func empty_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(99),
u8.truncate(96),
u8.truncate(24),
u8.truncate(5),
u8.truncate(163),
u8.truncate(96),
u8.truncate(20),
u8.truncate(140),
u8.truncate(84),
u8.truncate(0),
u8.truncate(0),
u8.truncate(46),
u8.truncate(175),
u8.truncate(181),
u8.truncate(239),
u8.truncate(0),
u8.truncate(4),
u8.truncate(0),
u8.truncate(0),
]
}
func archive_with_trailing_decoded_bytes(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(99),
u8.truncate(96),
u8.truncate(24),
u8.truncate(5),
u8.truncate(163),
u8.truncate(96),
u8.truncate(20),
u8.truncate(140),
u8.truncate(130),
u8.truncate(81),
u8.truncate(48),
u8.truncate(18),
u8.truncate(1),
u8.truncate(0),
u8.truncate(126),
u8.truncate(225),
u8.truncate(28),
u8.truncate(108),
u8.truncate(0),
u8.truncate(6),
u8.truncate(0),
u8.truncate(0),
]
}
func duplicate_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(237),
u8.truncate(211),
u8.truncate(49),
u8.truncate(10),
u8.truncate(131),
u8.truncate(64),
u8.truncate(16),
u8.truncate(5),
u8.truncate(208),
u8.truncate(57),
u8.truncate(74),
u8.truncate(78),
u8.truncate(32),
u8.truncate(134),
u8.truncate(136),
u8.truncate(158),
u8.truncate(103),
u8.truncate(139),
u8.truncate(180),
u8.truncate(10),
u8.truncate(113),
u8.truncate(69),
u8.truncate(143),
u8.truncate(239),
u8.truncate(154),
u8.truncate(74),
u8.truncate(236),
u8.truncate(13),
u8.truncate(132),
u8.truncate(125),
u8.truncate(175),
u8.truncate(249),
u8.truncate(195),
u8.truncate(111),
u8.truncate(166),
u8.truncate(250),
u8.truncate(169),
u8.truncate(201),
u8.truncate(91),
u8.truncate(142),
u8.truncate(123),
u8.truncate(181),
u8.truncate(69),
u8.truncate(223),
u8.truncate(117),
u8.truncate(223),
u8.truncate(44),
u8.truncate(174),
u8.truncate(89),
u8.truncate(188),
u8.truncate(78),
u8.truncate(247),
u8.truncate(209),
u8.truncate(247),
u8.truncate(195),
u8.truncate(115),
u8.truncate(136),
u8.truncate(71),
u8.truncate(27),
u8.truncate(63),
u8.truncate(176),
u8.truncate(204),
u8.truncate(57),
u8.truncate(125),
u8.truncate(202),
u8.truncate(203),
u8.truncate(168),
u8.truncate(211),
u8.truncate(52),
u8.truncate(190),
u8.truncate(131),
u8.truncate(122),
u8.truncate(37),
u8.truncate(251),
u8.truncate(175),
u8.truncate(122),
u8.truncate(255),
u8.truncate(121),
u8.truncate(157),
u8.truncate(140),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(224),
u8.truncate(143),
u8.truncate(237),
u8.truncate(184),
u8.truncate(102),
u8.truncate(234),
u8.truncate(233),
u8.truncate(0),
u8.truncate(40),
u8.truncate(0),
u8.truncate(0),
]
}
func traversal_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(237),
u8.truncate(205),
u8.truncate(65),
u8.truncate(10),
u8.truncate(194),
u8.truncate(48),
u8.truncate(20),
u8.truncate(4),
u8.truncate(208),
u8.truncate(127),
u8.truncate(20),
u8.truncate(79),
u8.truncate(80),
u8.truncate(163),
u8.truncate(9),
u8.truncate(122),
u8.truncate(158),
u8.truncate(32),
u8.truncate(217),
u8.truncate(75),
u8.truncate(211),
u8.truncate(130),
u8.truncate(199),
u8.truncate(55),
u8.truncate(186),
u8.truncate(18),
u8.truncate(247),
u8.truncate(10),
u8.truncate(165),
u8.truncate(239),
u8.truncate(109),
u8.truncate(102),
u8.truncate(152),
u8.truncate(205),
u8.truncate(76),
u8.truncate(211),
u8.truncate(177),
u8.truncate(245),
u8.truncate(91),
u8.truncate(189),
u8.truncate(183),
u8.truncate(248),
u8.truncate(157),
u8.truncate(52),
u8.truncate(92),
u8.truncate(74),
u8.truncate(121),
u8.truncate(231),
u8.truncate(240),
u8.truncate(157),
u8.truncate(195),
u8.truncate(233),
u8.truncate(163),
u8.truncate(191),
u8.truncate(246),
u8.truncate(107),
u8.truncate(206),
u8.truncate(231),
u8.truncate(56),
u8.truncate(164),
u8.truncate(248),
u8.truncate(131),
u8.truncate(181),
u8.truncate(47),
u8.truncate(117),
u8.truncate(30),
u8.truncate(151),
u8.truncate(177),
u8.truncate(79),
u8.truncate(143),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(96),
u8.truncate(139),
u8.truncate(158),
u8.truncate(41),
u8.truncate(2),
u8.truncate(244),
u8.truncate(212),
u8.truncate(0),
u8.truncate(40),
u8.truncate(0),
u8.truncate(0),
]
}
func unsupported_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(237),
u8.truncate(206),
u8.truncate(49),
u8.truncate(10),
u8.truncate(128),
u8.truncate(48),
u8.truncate(0),
u8.truncate(3),
u8.truncate(192),
u8.truncate(62),
u8.truncate(197),
u8.truncate(39),
u8.truncate(212),
u8.truncate(82),
u8.truncate(252),
u8.truncate(143),
u8.truncate(131),
u8.truncate(136),
u8.truncate(40),
u8.truncate(14),
u8.truncate(90),
u8.truncate(255),
u8.truncate(111),
u8.truncate(113),
u8.truncate(212),
u8.truncate(185),
u8.truncate(131),
u8.truncate(112),
u8.truncate(183),
u8.truncate(36),
u8.truncate(91),
u8.truncate(178),
u8.truncate(45),
u8.truncate(251),
u8.truncate(26),
u8.truncate(26),
u8.truncate(139),
u8.truncate(213),
u8.truncate(144),
u8.truncate(243),
u8.truncate(147),
u8.truncate(213),
u8.truncate(59),
u8.truncate(191),
u8.truncate(189),
u8.truncate(143),
u8.truncate(49),
u8.truncate(229),
u8.truncate(208),
u8.truncate(165),
u8.truncate(50),
u8.truncate(30),
u8.truncate(243),
u8.truncate(84),
u8.truncate(218),
u8.truncate(158),
u8.truncate(187),
u8.truncate(206),
u8.truncate(186),
u8.truncate(82),
u8.truncate(231),
u8.truncate(3),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(252),
u8.truncate(199),
u8.truncate(13),
u8.truncate(220),
u8.truncate(62),
u8.truncate(72),
u8.truncate(42),
u8.truncate(0),
u8.truncate(40),
u8.truncate(0),
u8.truncate(0),
]
}
func two_entry_archive(): Vec<u8> {
return Vec [
u8.truncate(31),
u8.truncate(139),
u8.truncate(8),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(2),
u8.truncate(255),
u8.truncate(237),
u8.truncate(209),
u8.truncate(203),
u8.truncate(9),
u8.truncate(128),
u8.truncate(48),
u8.truncate(16),
u8.truncate(4),
u8.truncate(208),
u8.truncate(45),
u8.truncate(197),
u8.truncate(18),
u8.truncate(114),
u8.truncate(8),
u8.truncate(73),
u8.truncate(61),
u8.truncate(177),
u8.truncate(4),
u8.truncate(63),
u8.truncate(253),
u8.truncate(27),
u8.truncate(60),
u8.truncate(6),
u8.truncate(241),
u8.truncate(166),
u8.truncate(8),
u8.truncate(190),
u8.truncate(119),
u8.truncate(153),
u8.truncate(189),
u8.truncate(205),
u8.truncate(194),
u8.truncate(180),
u8.truncate(120),
u8.truncate(94),
u8.truncate(234),
u8.truncate(74),
u8.truncate(206),
u8.truncate(103),
u8.truncate(118),
u8.truncate(99),
u8.truncate(94),
u8.truncate(220),
u8.truncate(37),
u8.truncate(213),
u8.truncate(18),
u8.truncate(83),
u8.truncate(122),
u8.truncate(225),
u8.truncate(183),
u8.truncate(216),
u8.truncate(215),
u8.truncate(173),
u8.truncate(45),
u8.truncate(189),
u8.truncate(50),
u8.truncate(254),
u8.truncate(105),
u8.truncate(254),
u8.truncate(234),
u8.truncate(254),
u8.truncate(213),
u8.truncate(254),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(0),
u8.truncate(119),
u8.truncate(14),
u8.truncate(245),
u8.truncate(189),
u8.truncate(237),
u8.truncate(39),
u8.truncate(0),
u8.truncate(40),
u8.truncate(0),
u8.truncate(0),
]
}
blocking func inspect_bytes(bytes: Vec<u8>, limits: ArchiveLimits): ArchiveReport! {
let source = FragmentedSource { bytes: move bytes, next: 0 }
return inspect(move source, limits)?
}
blocking func require_failure(
bytes: Vec<u8>,
limits: ArchiveLimits,
code: &str,
): void! {
let _unexpected = inspect_bytes(move bytes, limits) catch failure {
if failure.has_code(code) { return }
return error.new("archive-inspect.test", "inspection returned the wrong failure")
}
return error.new("archive-inspect.test", "invalid archive was accepted")
}
test valid_archive_is_published_only_after_complete_validation {
let report = inspect_bytes(valid_archive(), ArchiveLimits.standard())?
let path = report.entry_path(0) otherwise {
return error.new("archive-inspect.test", "validated path is absent")
}
let size = report.entry_size(0) otherwise {
return error.new("archive-inspect.test", "validated size is absent")
}
let directory = report.entry_is_directory(0) otherwise {
return error.new("archive-inspect.test", "validated kind is absent")
}
if report.entry_count() != 1
|| path != "a.txt"
|| size != 6
|| directory {
return error.new("archive-inspect.test", "validated report changed")
}
var damaged = valid_archive()
damaged[94] = 245
require_failure(
move damaged,
ArchiveLimits.standard(),
"std.compress.invalid_gzip",
)?
var truncated = valid_archive()
let truncated_len = truncated.len() - 1
truncated.truncate(truncated_len)
require_failure(
move truncated,
ArchiveLimits.standard(),
"std.compress.invalid_gzip",
)?
return
}
test every_resource_limit_is_enforced_before_report_publication {
require_failure(
valid_archive(),
ArchiveLimits.new(100, 268435456, 4096, 240)?,
"archive-inspect.input_limit",
)?
require_failure(
valid_archive(),
ArchiveLimits.new(67108864, 5, 4096, 240)?,
"archive-inspect.output_limit",
)?
require_failure(
valid_archive(),
ArchiveLimits.new(67108864, 268435456, 4096, 4)?,
"archive-inspect.path_limit",
)?
require_failure(
two_entry_archive(),
ArchiveLimits.new(67108864, 268435456, 1, 240)?,
"archive-inspect.entry_limit",
)?
let empty = inspect_bytes(
empty_archive(),
ArchiveLimits.new(67108864, 1024, 4096, 240)?,
)?
if empty.entry_count() != 0 {
return error.new("archive-inspect.test", "empty archive published an entry")
}
require_failure(
archive_with_trailing_decoded_bytes(),
ArchiveLimits.new(67108864, 1024, 4096, 240)?,
"archive-inspect.output_limit",
)?
return
}
test unsafe_and_ambiguous_entries_are_rejected {
require_failure(
duplicate_archive(),
ArchiveLimits.standard(),
"archive-inspect.duplicate_path",
)?
require_failure(
traversal_archive(),
ArchiveLimits.standard(),
"std.archive.invalid_tar",
)?
require_failure(
unsupported_archive(),
ArchiveLimits.standard(),
"archive-inspect.unsupported_entry",
)?
return
}