Skip to content

Add bundle version of utf8_range to validate attributes #3512

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ Increment the:
* [SDK] Implements options for the ParentBasedSampler with default values
[#3553](https://github.com/open-telemetry/opentelemetry-cpp/pull/3553)

* [SDK] Add bundle version of utf8_range to validate attributes
[#3512](https://github.com/open-telemetry/opentelemetry-cpp/pull/3512)

## [1.22 2025-07-11]

* [DOC] Udpate link to membership document
Expand Down
47 changes: 42 additions & 5 deletions exporters/otlp/src/otlp_populate_attribute_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h"
#include "opentelemetry/sdk/resource/resource.h"
#include "opentelemetry/version.h"
Expand Down Expand Up @@ -85,8 +86,18 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
}
else if (nostd::holds_alternative<nostd::string_view>(value))
{
proto_value->set_string_value(nostd::get<nostd::string_view>(value).data(),
nostd::get<nostd::string_view>(value).size());
if (allow_bytes &&
!opentelemetry::sdk::common::AttributeIsValidString(nostd::get<nostd::string_view>(value)))
{
proto_value->set_bytes_value(
reinterpret_cast<const void *>(nostd::get<nostd::string_view>(value).data()),
nostd::get<nostd::string_view>(value).size());
}
else
{
proto_value->set_string_value(nostd::get<nostd::string_view>(value).data(),
nostd::get<nostd::string_view>(value).size());
}
}
else if (nostd::holds_alternative<nostd::span<const uint8_t>>(value))
{
Expand Down Expand Up @@ -159,7 +170,15 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
auto array_value = proto_value->mutable_array_value();
for (const auto &val : nostd::get<nostd::span<const nostd::string_view>>(value))
{
array_value->add_values()->set_string_value(val.data(), val.size());
if (allow_bytes && !opentelemetry::sdk::common::AttributeIsValidString(val))
{
array_value->add_values()->set_bytes_value(reinterpret_cast<const void *>(val.data()),
val.size());
}
else
{
array_value->add_values()->set_string_value(val.data(), val.size());
}
}
}
}
Expand Down Expand Up @@ -224,7 +243,17 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
}
else if (nostd::holds_alternative<std::string>(value))
{
proto_value->set_string_value(nostd::get<std::string>(value));
if (allow_bytes &&
!opentelemetry::sdk::common::AttributeIsValidString(nostd::get<std::string>(value)))
{
proto_value->set_bytes_value(
reinterpret_cast<const void *>(nostd::get<std::string>(value).data()),
nostd::get<std::string>(value).size());
}
else
{
proto_value->set_string_value(nostd::get<std::string>(value));
}
}
else if (nostd::holds_alternative<std::vector<bool>>(value))
{
Expand Down Expand Up @@ -281,7 +310,15 @@ void OtlpPopulateAttributeUtils::PopulateAnyValue(
auto array_value = proto_value->mutable_array_value();
for (const auto &val : nostd::get<std::vector<std::string>>(value))
{
array_value->add_values()->set_string_value(val);
if (allow_bytes && !opentelemetry::sdk::common::AttributeIsValidString(val))
{
array_value->add_values()->set_bytes_value(reinterpret_cast<const void *>(val.data()),
val.size());
}
else
{
array_value->add_values()->set_string_value(val);
}
}
}
}
Expand Down
129 changes: 129 additions & 0 deletions sdk/include/opentelemetry/sdk/common/attribute_validity.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>

#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/nostd/function_ref.h"
#include "opentelemetry/nostd/span.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace common
{

OPENTELEMETRY_EXPORT bool AttributeIsValidString(nostd::string_view value) noexcept;

/**
* Validate if an attribute value is valid.
*/
struct AttributeValidator
{
bool operator()(bool /*v*/) noexcept { return true; }
bool operator()(int32_t /*v*/) noexcept { return true; }
bool operator()(uint32_t /*v*/) noexcept { return true; }
bool operator()(int64_t /*v*/) noexcept { return true; }
bool operator()(uint64_t /*v*/) noexcept { return true; }
bool operator()(double /*v*/) noexcept { return true; }
bool operator()(nostd::string_view v) noexcept { return AttributeIsValidString(v); }
bool operator()(std::string v) noexcept { return AttributeIsValidString(v); }
bool operator()(const char *v) noexcept { return AttributeIsValidString(v); }
bool operator()(nostd::span<const uint8_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const bool> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const int32_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const uint32_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const int64_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const uint64_t> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const double> /*v*/) noexcept { return true; }
bool operator()(nostd::span<const nostd::string_view> v) noexcept
{
for (const auto &s : v)
{
if (!AttributeIsValidString(s))
{
return false;
}
}
return true;
}
bool operator()(const std::vector<bool> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<int32_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<uint32_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<int64_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<double> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<std::string> &v)
{
for (const auto &s : v)
{
if (!AttributeIsValidString(s))
{
return false;
}
}
return true;
}
bool operator()(const std::vector<uint64_t> & /*v*/) noexcept { return true; }
bool operator()(const std::vector<uint8_t> & /*v*/) noexcept { return true; }

OPENTELEMETRY_EXPORT static bool IsValid(const std::string &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(nostd::string_view value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(const OwnedAttributeValue &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsValid(
const opentelemetry::common::AttributeValue &value) noexcept;

OPENTELEMETRY_EXPORT static bool IsAllValid(const AttributeMap &attributes) noexcept;

OPENTELEMETRY_EXPORT static bool IsAllValid(const OrderedAttributeMap &attributes) noexcept;

OPENTELEMETRY_EXPORT static void Filter(AttributeMap &attributes, nostd::string_view log_hint);

OPENTELEMETRY_EXPORT static void Filter(OrderedAttributeMap &attributes,
nostd::string_view log_hint);
};

/**
* Supports internal iteration over a collection of key-value pairs and filtering of invalid
* attributes.
*/
class OPENTELEMETRY_EXPORT KeyValueFilterIterable : public opentelemetry::common::KeyValueIterable
{
public:
KeyValueFilterIterable(const opentelemetry::common::KeyValueIterable &origin,
opentelemetry::nostd::string_view log_hint) noexcept;

~KeyValueFilterIterable() override;

bool ForEachKeyValue(
opentelemetry::nostd::function_ref<bool(opentelemetry::nostd::string_view,
opentelemetry::common::AttributeValue)> callback)
const noexcept override;

size_t size() const noexcept override;

private:
// Pointer to the original KeyValueIterable
const opentelemetry::common::KeyValueIterable *origin_;

// Size of valid attributes
mutable size_t size_;

// Log hint for invalid attributes
opentelemetry::nostd::string_view log_hint_;
};

} // namespace common
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "opentelemetry/nostd/unique_ptr.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/common/global_log_handler.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
Expand Down Expand Up @@ -42,6 +44,7 @@ class InstrumentationScope
nostd::string_view schema_url = "",
InstrumentationScopeAttributes &&attributes = {})
{
common::AttributeValidator::Filter(attributes, "[InstrumentationScope]");
return nostd::unique_ptr<InstrumentationScope>(
new InstrumentationScope{name, version, schema_url, std::move(attributes)});
}
Expand All @@ -60,8 +63,19 @@ class InstrumentationScope
nostd::string_view schema_url,
const InstrumentationScopeAttributes &attributes)
{
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(attributes)});
// Copy attributes only when we find some invalid attributes and try to remove them.
if (common::AttributeValidator::IsAllValid(attributes))
{
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(attributes)});
}
else
{
InstrumentationScopeAttributes copy_attributes = attributes;
common::AttributeValidator::Filter(copy_attributes, "[InstrumentationScope]");
return nostd::unique_ptr<InstrumentationScope>(new InstrumentationScope{
name, version, schema_url, InstrumentationScopeAttributes(copy_attributes)});
}
}

/**
Expand All @@ -88,6 +102,19 @@ class InstrumentationScope
result->attributes_.reserve(opentelemetry::nostd::size(arg));
for (auto &argv : arg)
{
if (!common::AttributeValidator::IsValid(argv.first))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute key "
<< std::string{argv.first} << ". This attribute will be ignored.");
continue;
}

if (!common::AttributeValidator::IsValid(argv.second))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute value for "
<< std::string{argv.first} << ". This attribute will be ignored.");
continue;
}
result->SetAttribute(argv.first, argv.second);
}

Expand Down Expand Up @@ -148,6 +175,19 @@ class InstrumentationScope
void SetAttribute(nostd::string_view key,
const opentelemetry::common::AttributeValue &value) noexcept
{
if (!common::AttributeValidator::IsValid(key))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute key "
<< std::string{key} << ". This attribute will be ignored.");
return;
}

if (!common::AttributeValidator::IsValid(value))
{
OTEL_INTERNAL_LOG_WARN("[InstrumentationScope] Invalid attribute value for "
<< std::string{key} << ". This attribute will be ignored.");
return;
}
attributes_[std::string(key)] =
nostd::visit(opentelemetry::sdk::common::AttributeConverter(), value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "opentelemetry/common/attribute_value.h"
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/attribute_validity.h"
#include "opentelemetry/sdk/metrics/state/filtered_ordered_attribute_map.h"
#include "opentelemetry/version.h"

Expand Down Expand Up @@ -50,7 +51,8 @@ class DefaultAttributesProcessor : public AttributesProcessor
MetricAttributes process(
const opentelemetry::common::KeyValueIterable &attributes) const noexcept override
{
MetricAttributes result(attributes);
MetricAttributes result(
opentelemetry::sdk::common::KeyValueFilterIterable(attributes, "[Metrics] "));
return result;
}

Expand Down Expand Up @@ -78,7 +80,9 @@ class FilteringAttributesProcessor : public AttributesProcessor
const opentelemetry::common::KeyValueIterable &attributes) const noexcept override
{
MetricAttributes result;
attributes.ForEachKeyValue(
opentelemetry::sdk::common::KeyValueFilterIterable validate_attributes{attributes,
"[Metrics] "};
validate_attributes.ForEachKeyValue(
[&](nostd::string_view key, opentelemetry::common::AttributeValue value) noexcept {
if (allowed_attribute_keys_.find(key.data()) != allowed_attribute_keys_.end())
{
Expand Down
30 changes: 30 additions & 0 deletions sdk/src/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,36 @@

package(default_visibility = ["//visibility:public"])

cc_library(
name = "utf8_range",
srcs = [
"internal/utf8_range/uft8_range.cc",
],
hdrs = [
"internal/utf8_range/utf8_range.h",
"internal/utf8_range/utf8_range_neon.inc",
"internal/utf8_range/utf8_range_sse.inc",
],
include_prefix = "src/common",
deps = [
"//api",
],
)

cc_library(
name = "attribute_validity",
srcs = [
"attribute_validity.cc",
],
include_prefix = "src/common",
deps = [
"//api",
"//sdk:headers",
"//sdk/src/common:global_log_handler",
"//sdk/src/common:utf8_range",
],
)

cc_library(
name = "random",
srcs = [
Expand Down
10 changes: 8 additions & 2 deletions sdk/src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
# Copyright The OpenTelemetry Authors
# SPDX-License-Identifier: Apache-2.0

set(COMMON_SRCS random.cc global_log_handler.cc env_variables.cc base64.cc
disabled.cc)
set(COMMON_SRCS
random.cc
global_log_handler.cc
env_variables.cc
base64.cc
disabled.cc
attribute_validity.cc
internal/utf8_range/uft8_range.cc)
if(WIN32)
list(APPEND COMMON_SRCS platform/fork_windows.cc)
else()
Expand Down
Loading
Loading