| // 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 "validator/tracker.h" |
| |
| #include <string> |
| |
| #include "absl/container/flat_hash_set.h" |
| #include "absl/log/check.h" |
| #include "absl/strings/match.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "constants/labels.h" |
| #include "constants/status_codes.h" |
| #include "proto/manifest.pb.h" |
| #include "proto/validation_status.pb.h" |
| |
| namespace credentio { |
| namespace { |
| |
| std::string CreateUrl(absl::string_view manifest_label, |
| absl::string_view assertion_label, |
| absl::string_view url) { |
| if (url.empty()) { |
| if (assertion_label.empty()) { |
| return absl::StrCat("self#jumbf=/c2pa/", manifest_label); |
| } |
| return absl::StrCat("self#jumbf=/c2pa/", manifest_label, |
| "/c2pa.assertions/", assertion_label); |
| } |
| if (absl::StartsWith(url, "self#jumbf=")) { |
| return std::string(url); |
| } |
| return absl::StrCat("self#jumbf=/c2pa/", manifest_label, |
| kManifestLabelDelimiter, url); |
| } |
| |
| // Returns true if the label is well-formed. Used in DCHECKs to help catch bugs |
| // where a URI or path is provided instead of a label. |
| bool ValidLabel(absl::string_view label) { |
| return !label.empty() && !absl::StrContains(label, "/"); |
| } |
| |
| } // namespace |
| |
| ValidationTracker::ValidationTracker(Manifest* manifest) |
| : manifest_label_(manifest->label()), |
| status_set_(manifest->mutable_validation()), |
| id_("root") { |
| DCHECK(ValidLabel(manifest_label_)) << "label: " << manifest_label_; |
| } |
| |
| ValidationTracker::ValidationTracker(absl::string_view manifest_label, |
| ValidationStatusSet* status_set) |
| : manifest_label_(manifest_label), status_set_(status_set), id_("root") { |
| DCHECK(ValidLabel(manifest_label_)) << "label: " << manifest_label_; |
| } |
| |
| ValidationTracker ValidationTracker::AssertionTracker( |
| absl::string_view assertion_label) { |
| ValidationTracker new_tracker = *this; |
| new_tracker.assertion_label_ = assertion_label; |
| return new_tracker; |
| } |
| |
| void ValidationTracker::RecordFailure(FailureStatusCode status, |
| RecordOptions options) { |
| ValidationStatus* failure = status_set_->mutable_failures()->Add(); |
| failure->set_code(absl::StrCat(status)); |
| failure->set_url(CreateUrl(manifest_label_, assertion_label_, options.url)); |
| if (!options.explanation.empty()) { |
| failure->set_explanation(options.explanation); |
| } |
| } |
| |
| void ValidationTracker::RecordSuccess(SuccessStatusCode status, |
| RecordOptions options) { |
| ValidationStatus* success = status_set_->mutable_successes()->Add(); |
| success->set_code(absl::StrCat(status)); |
| success->set_url(CreateUrl(manifest_label_, assertion_label_, options.url)); |
| if (!options.explanation.empty()) { |
| success->set_explanation(options.explanation); |
| } |
| } |
| |
| void ValidationTracker::RecordInformational(InformationalStatusCode status, |
| RecordOptions options) { |
| ValidationStatus* informational = |
| status_set_->mutable_informationals()->Add(); |
| informational->set_code(absl::StrCat(status)); |
| informational->set_url( |
| CreateUrl(manifest_label_, assertion_label_, options.url)); |
| if (!options.explanation.empty()) { |
| informational->set_explanation(options.explanation); |
| } |
| } |
| |
| bool ValidationTracker::HasFailures() const { |
| return status_set_->failures_size() > 0; |
| } |
| |
| absl::flat_hash_set<std::string> ValidationTracker::GetFailures() const { |
| absl::flat_hash_set<std::string> failures; |
| for (const auto& failure_status : status_set_->failures()) { |
| failures.insert(std::string(failure_status.code())); |
| } |
| return failures; |
| } |
| |
| absl::flat_hash_set<std::string> ValidationTracker::GetSuccesses() const { |
| absl::flat_hash_set<std::string> successes; |
| for (const auto& success_status : status_set_->successes()) { |
| successes.insert(std::string(success_status.code())); |
| } |
| return successes; |
| } |
| |
| absl::flat_hash_set<std::string> ValidationTracker::GetInformationals() const { |
| absl::flat_hash_set<std::string> informational; |
| for (const auto& informational_status : status_set_->informationals()) { |
| informational.insert(std::string(informational_status.code())); |
| } |
| return informational; |
| } |
| |
| void ValidationTracker::MergeStatuses(const ValidationStatusSet& statuses) { |
| for (const auto& failure : statuses.failures()) { |
| ValidationStatus* f = status_set_->mutable_failures()->Add(); |
| *f = failure; |
| f->set_url(CreateUrl(manifest_label_, assertion_label_, failure.url())); |
| } |
| for (const auto& success : statuses.successes()) { |
| ValidationStatus* s = status_set_->mutable_successes()->Add(); |
| *s = success; |
| s->set_url(CreateUrl(manifest_label_, assertion_label_, success.url())); |
| } |
| for (const auto& info : statuses.informationals()) { |
| ValidationStatus* i = status_set_->mutable_informationals()->Add(); |
| *i = info; |
| i->set_url(CreateUrl(manifest_label_, assertion_label_, info.url())); |
| } |
| } |
| |
| } // namespace credentio |