blob: 6c580e8c7d4ced40ef704360f1cb849fb6411805 [file] [edit]
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include "formats/tiff/extractor.h"
#include <sys/types.h>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/status_matchers.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "formats/asset_box.h"
#include "formats/byte_range.h"
#include "formats/extractor_result.h"
#include "formats/tiff/constants.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "riegeli/bytes/string_reader.h"
#include "utils/byte_writers.h"
namespace credentio {
namespace {
using ::absl_testing::IsOk;
using ::absl_testing::StatusIs;
using ::testing::Eq;
#ifndef ASSERT_OK_AND_ASSIGN
#define ASSERT_OK_AND_ASSIGN_CONCAT2(x, y) x##y
#define ASSERT_OK_AND_ASSIGN_CONCAT(x, y) ASSERT_OK_AND_ASSIGN_CONCAT2(x, y)
#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \
ASSERT_OK_AND_ASSIGN_IMPL(lhs, rexpr, __COUNTER__)
#define ASSERT_OK_AND_ASSIGN_IMPL(lhs, rexpr, id) \
auto ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id) = (rexpr); \
ASSERT_THAT(ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id), \
::absl_testing::IsOk()); \
lhs = std::move(*ASSERT_OK_AND_ASSIGN_CONCAT(status_or_, id))
#endif
using ::testing::HasSubstr;
struct TestCase {
std::string name;
absl::StatusOr<std::string> contents;
absl::StatusOr<ExtractorResult> result;
};
void WriteTiffHeader(uint32_t next_ifd_offset, std::vector<uint8_t>& data) {
WriteUint16NetworkOrder(kTiffBigEndian, &data); // Endian marker
WriteUint16NetworkOrder(kTiffMarker, &data); // TIFF marker
WriteUint32NetworkOrder(next_ifd_offset, &data); // Next IFD offset
}
void WriteString(absl::string_view contents, std::vector<uint8_t>& data) {
for (int i = 0; i < contents.size(); ++i) {
data.push_back(contents[i]);
}
}
struct Ifde {
uint16_t tag;
uint16_t type;
uint32_t count;
uint32_t value;
};
void WriteIfd(std::vector<Ifde> entries, uint32_t next_ifd,
std::vector<uint8_t>& data) {
WriteUint16NetworkOrder(entries.size(), &data); // IFD entry count
for (const Ifde& entry : entries) {
WriteUint16NetworkOrder(entry.tag, &data); // Tag
WriteUint16NetworkOrder(entry.type, &data); // Type
WriteUint32NetworkOrder(entry.count, &data); // Count
WriteUint32NetworkOrder(entry.value, &data); // Value
}
WriteUint32NetworkOrder(next_ifd, &data); // Next IFD offset
}
void WriteUint16(std::vector<uint16_t> entries, std::vector<uint8_t>& data) {
for (const uint16_t entry : entries) {
WriteUint16NetworkOrder(entry, &data);
}
}
void WriteUint32(std::vector<uint32_t> entries, std::vector<uint8_t>& data) {
for (const uint32_t entry : entries) {
WriteUint32NetworkOrder(entry, &data);
}
}
class ExtractorTest : public testing::TestWithParam<TestCase> {
public:
void SetUp() override { ASSERT_THAT(GetParam().contents.status(), IsOk()); }
};
INSTANTIATE_TEST_SUITE_P(
ExtractorTests, ExtractorTest,
testing::ValuesIn(
{TestCase{
.name = "EmptyContent",
.contents = "",
.result = absl::DataLossError("kUnexpectedEof"),
},
TestCase{
.name = "InvalidEndianMarker",
.contents = "\x12\x34",
.result = absl::DataLossError("endianness"),
},
TestCase{
.name = "InvalidTiffMarker",
.contents = "\x4d\x4d\x12\x34",
.result = absl::DataLossError("invalid endianness"),
},
TestCase{
.name = "SingleIfdNoEntries",
.contents =
[]() {
std::vector<uint8_t> data = {};
WriteTiffHeader(8, data);
WriteIfd({}, 0, data);
return std::string(data.begin(), data.end());
}(),
.result = absl::NotFoundError("No manifest store found"),
},
TestCase{
.name = "SingleIfdNoC2paEntry",
.contents =
[]() {
std::vector<uint8_t> data = {};
WriteTiffHeader(8, data);
WriteIfd({{.tag = 0x1234,
.type = 4,
.count = 1,
.value = 0x12345678}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = absl::NotFoundError("No manifest store found"),
},
TestCase{
.name = "ValidThreeValueOffsetTag",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(45, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 33) Offset Data
WriteUint16({0x1234, 0x5678, 0x9abc}, data);
// [33, 39) Offset Val
WriteUint16({27, 29, 31}, data);
// [39, 45) Counts Val
WriteUint16({2, 2, 2}, data);
// [45, 75) IFD
WriteIfd(
{{.tag = 0x0111, .type = 3, .count = 3, .value = 33},
{.tag = 0x0117, .type = 3, .count = 3, .value = 39}},
75, data);
// [75, 93) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "EntryWithMultipleUint8Values",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) First IFD
WriteIfd({Ifde{.tag = 0x1234,
.type = 1,
.count = 3,
.value = 0x01020300}},
45, data);
// [45, 61) Second IFD
WriteIfd({Ifde{.tag = kTiffTagC2pa,
.type = 0x0007,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "EntryWithMultipleUint16Values",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) First IFD
WriteIfd({Ifde{.tag = 0x1234,
.type = 3,
.count = 2,
.value = 0x01020304}},
45, data);
// [45, 61) Second IFD
WriteIfd({Ifde{.tag = kTiffTagC2pa,
.type = 0x0007,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "ValidSingleValueOffset",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(31, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 31) Offset Data
WriteUint32({0x12345678}, data);
// [31, 73) IFD
WriteIfd(
{
{.tag = 0x0111, .type = 4, .count = 1, .value = 27},
{.tag = 0x0115, .type = 4, .count = 1, .value = 4},
{.tag = 0x0117, .type = 4, .count = 1, .value = 4},
},
73, data);
// [73, 91) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "ValidSingleValueOffsetWithExtraData",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(31, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 31) Offset Data
WriteUint32({0x12345678}, data);
// [31, 73) IFD
WriteIfd(
{
{.tag = 0x0111, .type = 4, .count = 1, .value = 27},
{.tag = 0x0115, .type = 4, .count = 1, .value = 4},
{.tag = 0x0117, .type = 4, .count = 1, .value = 4},
},
73, data);
// [73, 91) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
// [91, 101) Extra Data (not part of the IFD)
return std::string(data.begin(), data.end()) + "extra_data";
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "ValidSingleSubIfd",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(45, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) Sub IFD
WriteIfd({{.tag = 0x1234,
.type = 4,
.count = 1,
.value = 0x12345678}},
0, data);
// [45, 63) IFD
WriteIfd(
{{.tag = 0x014a, .type = 4, .count = 1, .value = 27}},
63, data);
// [63, 81) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "ValidTwoLayerSubIfd",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(63, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) Child Sub IFD
WriteIfd({{.tag = 0x1234,
.type = 4,
.count = 1,
.value = 0x12345678}},
0, data);
// [45, 63) Parent Sub IFD
WriteIfd(
{{.tag = 0x014a, .type = 4, .count = 1, .value = 27}}, 0,
data);
// [63, 81) First Base IFD
WriteIfd(
{{.tag = 0x014a, .type = 4, .count = 1, .value = 45}},
81, data);
// [81, 99) Second Base IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "ValidSubWithChainedIfd",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(63, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) Second Sub IFD
WriteIfd({{.tag = 0x1234,
.type = 4,
.count = 1,
.value = 0x12345678}},
0, data);
// [45, 63) First Sub IFD
WriteIfd({{.tag = 0x1234,
.type = 4,
.count = 1,
.value = 0x12345678}},
27, data);
// [63, 81) First Base IFD
WriteIfd(
{{.tag = 0x014a, .type = 4, .count = 1, .value = 45}},
81, data);
// [81, 99) Second Base IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = ExtractorResult{.manifest_store = "test_manifest_store",
.asset_byte_info =
{.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
// This is technically not a valid TIFF file, as there is no other
// data, but it abides by the C2PA spec.
.name = "SingleIfdOnlyC2paEntry",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result =
ExtractorResult{
.manifest_store = "test_manifest_store",
.asset_byte_info = {.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "SingleIfdC2paEntryBeforeOtherEntry",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd(
{{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8},
{.tag = 0x1234, .type = 1, .count = 1, .value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result =
ExtractorResult{
.manifest_store = "test_manifest_store",
.asset_byte_info = {.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "SingleIfdC2paEntryAfterOtherEntry",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = 0x1234, .type = 1, .count = 1, .value = 8},
{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result =
ExtractorResult{
.manifest_store = "test_manifest_store",
.asset_byte_info = {.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "MultipleIfdC2paInFirstIfd",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
45, data);
// [45, 81) Second IFD
WriteIfd(
{{.tag = 0x1234, .type = 1, .count = 1, .value = 8}}, 0,
data);
return std::string(data.begin(), data.end());
}(),
.result =
absl::NotFoundError("Manifest Store must be in the last IFD"),
},
TestCase{
.name = "MultipleIfdC2paInSecondIfd",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd(
{{.tag = 0x1234, .type = 1, .count = 1, .value = 8}}, 45,
data);
// [45, 81) Second IFD
WriteIfd({{.tag = 0x1234, .type = 1, .count = 1, .value = 8},
{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result = absl::NotFoundError(
"Manifest Store must be the only entry in the IFD"),
},
TestCase{
.name = "UnknownIfdEntryType",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = 0x1234,
.type = 0x1234,
.count = 1,
.value = 8}},
45, data);
// [45, 81) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result =
ExtractorResult{
.manifest_store = "test_manifest_store",
.asset_byte_info = {.manifest_store_location =
{.offset = 8, .length = 19}}},
},
TestCase{
.name = "MultipleIfdC2paInBoth",
.contents =
[]() {
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(49, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 49) Manifest Store
WriteString("another_manifest_store", data);
// [49, 67) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
67, data);
// [67, 85) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}(),
.result =
absl::NotFoundError("Manifest Store must be in the last IFD"),
},
TestCase{
.name = "FuzzTestRegression_b421332332",
.contents =
std::string("MM\000*"
"\000\000\000\t\000\000\001\001JJJJ\303\303"
"\004\000\000\000\t\004\000\000\000",
27),
.result = absl::InternalError("Failed to seek to offs"),
},
{
.name = "FuzzTestRegression_b422060716",
.contents = std::string(
"MM\000*\000\000\000\t\000\000\002\001 "
"\000\003\000\000\000\002\000\000\000\000\001!"
"\000\004\000\000kkkkkkk\000..\000\000\000\303s\000\000",
46),
.result = absl::InternalError("Failed to seek to offset"),
},
{
.name = "FuzzTestRegression_SmallC2paSize",
.contents = std::string(
"II*"
"\000e\000\000\000\014\001\232\001\014\000\000\356\000\000\224"
"\000\000\000\001A\002\000l\205\000\0004\000\000\000\002\210"
"\007\000\005\000@"
"\304\214\000\270\000\000\300\n\377\305\324\200\377\377\000b"
"\000\000\000\241\001\003\000pT@"
"\225\257R\000\000J\001\004\000\001\003y\000\326\000\000\000"
"\373\377\373\372\005\022\000\000\254\322\333\000\000\035\001"
"\003\000\001\000A\315\007\000\004\000\000\000\362\001\000\000"
"\000\000\000\000\t\343\337\000\2372\010\000\000\211\000\000"
"\000\000\371\000\000\000r\000\000\200\340\005\000\017\004\000"
"\000\034\010\000\000\000\027\000\230X\353\233\005\270\372=*"
"\032\006\3768\332\034%"
"\000\000\000\000N\310\000\000\332\357\213\213\020@\003 "
"\000\242\027\000\001&"
"\023\316\000E\000\000\000\000\000\002\250\001\001\345\226\000"
"\000\000\000\000\200\000\000\000u\202)"
"\030\327u\000\000\000\000\000\000\000\000\000\000\000\000\000"
"\000\000\000\372\372\373\000\373PPPP\325\000\000\336@"
"\000\000JJ\037\265\345S\000AA\003\332\376\000U\266\004\000"
"\000\000\000\000\000\000\200\0148\007\000\000\000\000\000\000"
"\377\377\177\375\255\351\205\003\000\000\273\001^"
"\004E\201\034\211\371Iw\325\234\377\252\032S.\006n\344$"
"\277\036\000\006;t)\324\307@)\332f\003\001\000[\000\246\307b "
"l\177\010\000\000\000\000\257\354\334\244\243\243\243\243\243"
"\243\243\243\332\332\332\243\247\243\243\243\243\243\243\243"
"\243\243\243\243\243\243\243\243\243\243\315\243\243\243\243"
"\243\243\243\243\243\243\243\247\250\247{"
"\253\236\241\243\000\000\244\302,\230\230\230\230-"
"1\002x\361xx\221\221\000\000\000\346\000m\2544\024\000\030"
"\000\000\000\000\000\000\367\267AAAAAAAA\264\366|\304\264!"
"\000\005\355\000\000\000\367\372\236#"
"\374\000\230\300\300\300\300\300\300\300\300\300\300\300\300"
"\300\300\300\2304\000\000\000",
487),
.result =
ExtractorResult{
.manifest_store = std::string("\xF2\x01\x00\x00", 4),
.asset_byte_info = {.manifest_store_location =
{.offset = 111, .length = 4}}},
}}),
[](const testing::TestParamInfo<ExtractorTest::ParamType>& info) {
return info.param.name;
});
TEST_P(ExtractorTest, ExtractManifestStore) {
const TestCase& test_case = GetParam();
ASSERT_OK_AND_ASSIGN(std::string contents, test_case.contents);
riegeli::StringReader<> input(contents);
absl::StatusOr<std::string> result =
TiffExtractor().ExtractManifestStore(input);
const auto& expected_result = test_case.result;
if (expected_result.ok()) {
ASSERT_THAT(result, IsOk());
EXPECT_THAT(*result, Eq(expected_result->manifest_store));
} else {
EXPECT_THAT(result,
StatusIs(expected_result.status().code(),
HasSubstr(expected_result.status().message())));
}
}
TEST_P(ExtractorTest, ExtractManifestStoreLocation) {
const TestCase& test_case = GetParam();
ASSERT_OK_AND_ASSIGN(std::string contents, test_case.contents);
riegeli::StringReader<> input(contents);
absl::StatusOr<std::optional<ByteRange>> result =
TiffExtractor().ExtractManifestStoreLocation(input, {});
const auto& expected_result = test_case.result;
if (expected_result.ok()) {
ASSERT_THAT(result, IsOk());
EXPECT_THAT(*result,
Eq(expected_result->asset_byte_info.manifest_store_location));
} else {
EXPECT_THAT(result,
StatusIs(expected_result.status().code(),
HasSubstr(expected_result.status().message())));
}
}
TEST_P(ExtractorTest, ExtractBoxes) {
ASSERT_OK_AND_ASSIGN(std::string contents, GetParam().contents);
riegeli::StringReader<> input(contents);
absl::StatusOr<std::vector<AssetBox>> result =
TiffExtractor().ExtractBoxes(input, {});
EXPECT_THAT(
result,
StatusIs(absl::StatusCode::kUnimplemented,
HasSubstr("Extracting boxes is not supported for TIFF files.")));
}
TEST(OffsetTiffExtractorTest, FailsWhenEndOffsetIsSmallerThanAsset) {
std::string contents = []() {
// Adding a prefix to the contents, need to offset all the offsets by 7.
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}();
riegeli::StringReader<> input(contents);
EXPECT_THAT(
TiffExtractor().ExtractManifestStoreLocation(input, {.end_offset = 20}),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("TiffExtractor::ExtractManifestStoreLocation only "
"supports operations over the entire file.")));
}
TEST(OffsetTiffExtractorTest,
FailsWhenEndOffsetEqualToAssetSizeAndCurrentOffsetIsNonZero) {
std::string contents = []() {
// Adding a prefix to the contents, need to offset all the offsets by 7.
std::vector<uint8_t> data = {};
// [0, 8) Tiff Header
WriteTiffHeader(27, data);
// [8, 27) Manifest Store
WriteString("test_manifest_store", data);
// [27, 45) IFD
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 19,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}();
riegeli::StringReader<> input(contents);
ASSERT_TRUE(input.Seek(7));
EXPECT_THAT(
TiffExtractor().ExtractManifestStoreLocation(
input, {.end_offset = static_cast<int64_t>(contents.size())}),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("TiffExtractor::ExtractManifestStoreLocation only "
"supports operations over the entire file.")));
}
TEST(TiffExtractorTest, UnboundedManifestStoreAllocation) {
std::string contents = []() {
std::vector<uint8_t> data = {};
WriteTiffHeader(13, data);
WriteString("short", data);
WriteIfd({{.tag = kTiffTagC2pa,
.type = kTiffTypeUndefined,
.count = 11 * 1024 * 1024,
.value = 8}},
0, data);
return std::string(data.begin(), data.end());
}();
riegeli::StringReader<> input(contents);
EXPECT_THAT(TiffExtractor().ExtractManifestStore(input),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("Manifest store is too large")));
}
} // namespace
} // namespace credentio