| // 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 "assertion/actions_assertion_validator.h" | |
| #include <string> | |
| #include "absl/strings/str_format.h" | |
| #include "absl/strings/string_view.h" | |
| #include "constants/status_codes.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "proto/manifest.pb.h" | |
| #include "testing/proto_test_utils.h" | |
| #include "testing/test_validation_tracker.h" | |
| namespace credentio { | |
| namespace { | |
| using ::credentio_testing::ParseTextProtoOrDie; | |
| using ::testing::IsEmpty; | |
| using ::testing::TestWithParam; | |
| using ::testing::ValuesIn; | |
| // Returns the appropriate ingredient relationship for the given action. | |
| std::string IngredientRelation(absl::string_view action) { | |
| return (action == "c2pa.placed" || action == "c2pa.removed") ? "componentOf" | |
| : "parentOf"; | |
| } | |
| TEST(ActionsAssertionValidatorTest, Okay) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { actions { action: "c2pa.created" } } | |
| } | |
| )pb"); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), IsEmpty()); | |
| } | |
| TEST(ActionsAssertionValidatorTest, OkayV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions" } | |
| } | |
| assertions { | |
| label: "c2pa.actions" | |
| actions_v1 { actions { action: "c2pa.created" } } | |
| } | |
| )pb"); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), IsEmpty()); | |
| } | |
| TEST(ActionsAssertionValidatorTest, NoActions) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { actions {} } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, NoActionsV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { actions_v1 {} } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, CreatedNotFirst) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions { | |
| actions { action: "c2pa.drawing" } | |
| actions { action: "c2pa.created" } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, CreatedNotFirstV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions_v1 { | |
| actions { action: "c2pa.drawing" } | |
| actions { action: "c2pa.created" } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, OpenedNotFirst) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions { | |
| actions { action: "c2pa.drawing" } | |
| actions { action: "c2pa.opened" } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, OpenedNotFirstV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions_v1 { | |
| actions { action: "c2pa.drawing" } | |
| actions { action: "c2pa.opened" } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, EmptyAction) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { actions { actions { action: "" } } } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, EmptyActionV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { actions_v1 { actions { action: "" } } } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| struct ActionParam { | |
| std::string action_name; | |
| }; | |
| using ValidateIngredientTest = TestWithParam<ActionParam>; | |
| TEST_P(ValidateIngredientTest, MissingParameters) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>( | |
| absl::StrFormat(R"pb( | |
| assertions { actions { actions { action: "%s" } } } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, MissingParametersV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>( | |
| absl::StrFormat(R"pb( | |
| assertions { actions_v1 { actions { action: "%s" } } } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, MissingIngredients) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = | |
| ParseTextProtoOrDie<Manifest>(absl::StrFormat(R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters {} | |
| } | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, MissingIngredientsV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = | |
| ParseTextProtoOrDie<Manifest>(absl::StrFormat(R"pb( | |
| assertions { | |
| actions_v1 { | |
| actions { | |
| action: "%s" | |
| parameters {} | |
| } | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, EmptyIngredientUrl) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>( | |
| absl::StrFormat(R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { ingredients {} } | |
| } | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, EmptyIngredientUrlV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>( | |
| absl::StrFormat(R"pb( | |
| assertions { | |
| actions_v1 { | |
| actions { | |
| action: "%s" | |
| parameters { ingredients {} } | |
| } | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, MissingIngredientAssertion) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, WrongAssertionType) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| actions {} | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientTest, NoC2paManifestInIngredientV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "%s" } | |
| } | |
| )pb", | |
| GetParam().action_name, IngredientRelation(GetParam().action_name))); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| TEST_P(ValidateIngredientTest, NoC2paManifestInIngredientV2) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "%s" } | |
| } | |
| )pb", | |
| GetParam().action_name, IngredientRelation(GetParam().action_name))); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| TEST_P(ValidateIngredientTest, NoC2paManifestInIngredientV3) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "%s" } | |
| } | |
| )pb", | |
| GetParam().action_name, IngredientRelation(GetParam().action_name))); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| constexpr ActionParam kActionParams[] = { | |
| {.action_name = "c2pa.opened"}, {.action_name = "c2pa.placed"}, | |
| {.action_name = "c2pa.removed"}, {.action_name = "c2pa.repackaged"}, | |
| {.action_name = "c2pa.transcoded"}, | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(ActionsAssertionValidatorTest, ValidateIngredientTest, | |
| ValuesIn(kActionParams)); | |
| using ValidateIngredientParentOfTest = TestWithParam<ActionParam>; | |
| TEST_P(ValidateIngredientParentOfTest, Valid) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| relationship: "parentOf", | |
| active_manifest {} | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| TEST_P(ValidateIngredientParentOfTest, WrongRelationshipIngredientV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "childOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientParentOfTest, WrongRelationshipIngredientV2) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "childOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientParentOfTest, WrongRelationshipIngredientV3) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "childOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| constexpr ActionParam kParentOfActionParams[] = { | |
| {.action_name = "c2pa.opened"}, | |
| {.action_name = "c2pa.repackaged"}, | |
| {.action_name = "c2pa.transcoded"}, | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(ActionsAssertionValidatorTest, | |
| ValidateIngredientParentOfTest, | |
| ValuesIn(kParentOfActionParams)); | |
| using ValidateIngredientComponentOfTest = TestWithParam<ActionParam>; | |
| TEST_P(ValidateIngredientComponentOfTest, Valid) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| relationship: "componentOf", | |
| active_manifest {} | |
| } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| TEST_P(ValidateIngredientComponentOfTest, WrongRelationshipIngredientV1) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "parentOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientComponentOfTest, WrongRelationshipIngredientV2) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "parentOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| TEST_P(ValidateIngredientComponentOfTest, WrongRelationshipIngredientV3) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(absl::StrFormat( | |
| R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "%s" | |
| parameters { | |
| ingredients { | |
| url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { relationship: "parentOf" } | |
| } | |
| )pb", | |
| GetParam().action_name)); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionIngredientMismatch)); | |
| } | |
| constexpr ActionParam kComponentOfActionParams[] = { | |
| {.action_name = "c2pa.placed"}, | |
| {.action_name = "c2pa.removed"}, | |
| }; | |
| INSTANTIATE_TEST_SUITE_P(ActionsAssertionValidatorTest, | |
| ValidateIngredientComponentOfTest, | |
| ValuesIn(kComponentOfActionParams)); | |
| TEST(ActionsAssertionValidatorTest, ValidateRedactedActionMissingRedactedUri) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "c2pa.redacted" | |
| parameters {} | |
| } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionRedactionMismatch)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, | |
| ValidateRedactedActionMissingRedactedAssertion) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "c2pa.redacted" | |
| parameters { | |
| redacted_uri: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT( | |
| tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionRedactionMismatch)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, ValidateRedactedActionOkay) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| assertions { | |
| actions { | |
| actions { | |
| action: "c2pa.redacted" | |
| parameters { | |
| redacted_uri: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 {} | |
| } | |
| )pb"); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailureStatuses(), IsEmpty()); | |
| } | |
| TEST(ActionsAssertionValidatorTest, ValidCreatedAssertions) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2__1" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { actions { action: "c2pa.created" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2__1" | |
| actions { actions { action: "c2pa.resized" } } | |
| } | |
| )pb"); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), IsEmpty()); | |
| } | |
| TEST(ActionsAssertionValidatorTest, ValidOpenedAssertions) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| created_assertions { | |
| url: "self#jumbf=c2pa.assertions/c2pa.actions.v2__1" | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { | |
| actions { | |
| action: "c2pa.opened" | |
| parameters { | |
| ingredients { url: "self#jumbf=c2pa.assertions/c2pa.ingredient.v3" } | |
| } | |
| } | |
| } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2__1" | |
| actions { actions { action: "c2pa.resized" } } | |
| } | |
| assertions { | |
| label: "c2pa.ingredient.v3" | |
| ingredient_v3 { | |
| relationship: "parentOf", | |
| active_manifest {} | |
| } | |
| } | |
| )pb"); | |
| EXPECT_TRUE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), IsEmpty()); | |
| } | |
| TEST(ActionsAssertionValidatorTest, MalformedFirstAssertion) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { actions { action: "c2pa.resized" } } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, MalformedCreatedAssertions) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions" } | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| } | |
| assertions { | |
| label: "c2pa.actions" | |
| actions_v1 { actions { action: "c2pa.resized" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions { actions { action: "c2pa.created" } } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, MalformedOpenedAssertions) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions" } | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions_v1 { actions { action: "c2pa.resized" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions" | |
| actions { actions { action: "c2pa.opened" } } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| TEST(ActionsAssertionValidatorTest, MalformedDuplicatedCreatedAssertion) { | |
| TestValidationTracker tracker; | |
| ActionsAssertionValidator validator; | |
| auto manifest = ParseTextProtoOrDie<Manifest>(R"pb( | |
| claim { | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions" } | |
| created_assertions { url: "self#jumbf=c2pa.assertions/c2pa.actions.v2" } | |
| } | |
| assertions { | |
| label: "c2pa.actions.v2" | |
| actions_v1 { actions { action: "c2pa.created" } } | |
| } | |
| assertions { | |
| label: "c2pa.actions" | |
| actions { actions { action: "c2pa.created" } } | |
| } | |
| )pb"); | |
| EXPECT_FALSE(validator.Validate(manifest, tracker.tracker())); | |
| EXPECT_THAT(tracker.GetFailures(), | |
| ContainsFailure(FailureStatusCode::kAssertionActionMalformed)); | |
| } | |
| } // namespace | |
| } // namespace credentio |