use super::support::{File, Input, finish, memory, zip};
use std::sync::Arc;
use structured_asic::zip::{Archive, Integrity, ReadFailure};
use structured_asic::{
Limits, Session,
layout::{Census, Form, Inspection, Principal, Requirement, Rules},
media,
};
use structured_asn1::memory::{Memory, MemoryAccount};
#[test]
fn general_baseline_and_additional_file_layouts_do_not_collapse_into_one_rule_set() {
let input = Input::new(
zip(&[
File::stored(b"document.bin", b"payload"),
File::stored(b"META-INF/signature.p7s", b"not a CMS signature"),
File::stored(b"META-INF/certificate.cer", b"auxiliary"),
])
.bytes,
11,
);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let census = Census::read(&archive, &work).unwrap();
let form = Form::Simple(Principal::Cades);
let general = Inspection::read(&census, form, Rules::General, &work).unwrap();
assert_eq!(general.required_layout(), Some(true));
let baseline = Inspection::read(&census, form, Rules::BaselinePart1, &work).unwrap();
assert_eq!(baseline.required_layout(), Some(false));
assert!(
baseline
.issues(&work)
.any(|v| v.unwrap().requirement == Requirement::BaselineSimpleExtra)
);
let additional = Inspection::read(&census, form, Rules::AdditionalPart2, &work).unwrap();
assert_eq!(additional.required_layout(), None);
let member = archive.member(1, &work).unwrap().unwrap();
let content = finish(member.content(&work)).unwrap();
assert!(content.read(&work).is_err());
}
#[test]
fn e_xades_baseline_requires_original_manifest_and_never_becomes_part2() {
for present in [false, true] {
let mut files = vec![
File::stored(b"nested/document.bin", b"payload"),
File::stored(b"META-INF/signatures-1.xml", b"original XML"),
];
if present {
files.push(File::stored(
b"META-INF/manifest.xml",
b"original ODF manifest",
));
}
let input = Input::new(zip(&files).bytes, 17);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let census = Census::read(&archive, &work).unwrap();
let general =
Inspection::read(&census, Form::ExtendedXades, Rules::General, &work).unwrap();
assert_eq!(general.required_layout(), Some(true));
let baseline =
Inspection::read(&census, Form::ExtendedXades, Rules::BaselinePart1, &work).unwrap();
assert_eq!(baseline.required_layout(), Some(present));
let additional =
Inspection::read(&census, Form::ExtendedXades, Rules::AdditionalPart2, &work).unwrap();
assert_eq!(additional.required_layout(), None);
}
}
#[test]
fn additional_time_assertion_layout_retains_archive_renewal_files() {
let input = Input::new(
zip(&[
File::stored(b"document.bin", b"payload"),
File::stored(b"META-INF/timestamp.tst", b"initial"),
File::stored(b"META-INF/ASiCArchiveManifest-1.xml", b"archive"),
File::stored(b"META-INF/timestamp-2.tst", b"renewal"),
])
.bytes,
19,
);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let census = Census::read(&archive, &work).unwrap();
let additional = Inspection::read(
&census,
Form::Simple(Principal::Timestamp),
Rules::AdditionalPart2,
&work,
)
.unwrap();
assert_eq!(additional.required_layout(), Some(true));
let baseline = Inspection::read(
&census,
Form::Simple(Principal::Timestamp),
Rules::BaselinePart1,
&work,
)
.unwrap();
assert_eq!(baseline.required_layout(), None);
}
#[test]
fn renamed_archive_manifest_pattern_includes_the_normative_arbitrary_prefix() {
let input = Input::new(
zip(&[
File::stored(b"META-INF/old-ASiCArchiveManifest-1.xml", b"previous"),
File::stored(b"META-INF/OLD-ASICARCHIVEMANIFEST-1.xml", b"reserved"),
])
.bytes,
13,
);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let census = Census::read(&archive, &work).unwrap();
assert_eq!(
census.occurrence(0, &work).unwrap().unwrap().1,
structured_asic::layout::Role::ArchiveManifest
);
assert_eq!(
census.occurrence(1, &work).unwrap().unwrap().1,
structured_asic::layout::Role::Reserved
);
}
#[test]
fn absent_or_exhausted_mimetype_traversal_is_empty_even_under_zero_work() {
for present in [false, true] {
let files = if present {
vec![File::stored(b"mimetype", b"text/plain")]
} else {
Vec::new()
};
let input = Input::new(zip(&files).bytes, 31);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let census = Census::read(&archive, &work).unwrap();
let zero = Memory::new(Arc::new(MemoryAccount::new(0, 0)));
if present {
let one = Memory::new(Arc::new(MemoryAccount::new(0, 1)));
let mut headers = census.mimetypes(&one);
assert!(headers.next().unwrap().is_ok());
assert!(headers.next().is_none());
let mut refused = census.mimetypes(&zero);
assert!(refused.next().unwrap().is_err());
assert!(refused.next().is_none());
} else {
assert!(census.mimetypes(&zero).next().is_none());
}
}
}
#[test]
fn media_is_streamed_without_a_whole_value_buffer_and_requires_complete_crc() {
let value = format!(
"APPLICATION/vnd.etsi.asic-e+zip; name=\"{}\"",
"x".repeat(100_000)
);
let input = Input::new(
zip(&[File::stored(b"mimetype", value.as_bytes())]).bytes,
37,
);
let work = Memory::new(Arc::new(MemoryAccount::new(64 * 1024, 100_000_000)));
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let member = archive.member(0, &work).unwrap().unwrap();
let used = work.used();
let media = finish(member.read_media(&work)).unwrap();
assert_eq!(media.kind(), media::Type::Extended);
assert_eq!(media.member(), member.id());
assert_eq!(work.used(), used);
let mut corrupt = File::stored(b"mimetype", b"application/vnd.etsi.asic-s+zip");
corrupt.crc = Some(7);
let input = Input::new(zip(&[corrupt]).bytes, 3);
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let member = archive.member(0, &work).unwrap().unwrap();
assert!(matches!(
finish(member.read_media(&work)),
Err(ReadFailure::Integrity {
kind: Integrity::Crc,
..
})
));
}
#[test]
fn media_parameters_and_original_type_alternatives_are_not_rejected_by_substring_rules() {
for (value, expected) in [
(
"application/vnd.etsi.asic-s+zip; charset=utf-8",
media::Type::Simple,
),
(
"application/vnd.oasis.opendocument.text",
media::Type::Other,
),
(
"text/plain; name=\"application/vnd.etsi.asic-e+zip\"",
media::Type::Other,
),
(
"application/vnd.etsi.asic-e+zip;broken",
media::Type::Malformed,
),
("", media::Type::Malformed),
] {
let input = Input::new(zip(&[File::stored(b"mimetype", value.as_bytes())]).bytes, 3);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
let member = archive.member(0, &work).unwrap().unwrap();
assert_eq!(finish(member.read_media(&work)).unwrap().kind(), expected);
}
}