blob: ba65cfd9444372c750cad4a4d809e2840798fab6 [file]
// 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