Skip to content
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
2 changes: 1 addition & 1 deletion common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -640,8 +640,8 @@ cc_test(
deps = [
":source",
"//internal:testing",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings:cord",
"@com_google_absl//absl/types:optional",
],
)

Expand Down
43 changes: 38 additions & 5 deletions common/source.cc
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,18 @@ struct SourceTextTraits<absl::Cord> {

template <typename T>
absl::StatusOr<SourcePtr> NewSourceImpl(std::string description, const T& text,
const size_t text_size) {
const size_t text_size,
const size_t max_codepoints) {
if (ABSL_PREDICT_FALSE(
text_size >
static_cast<size_t>(std::numeric_limits<int32_t>::max()))) {
return absl::InvalidArgumentError("expression larger than 2GiB limit");
}
if ((text_size >> 2) > max_codepoints) {
// If byte size is 4 times the codepoint limit, then definitely exceeded.
return absl::InvalidArgumentError(absl::StrCat(
"expression is larger than codepoint limit ", max_codepoints));
}
using Traits = SourceTextTraits<T>;
size_t index = 0;
typename Traits::iterator_type it = Traits::Begin(text);
Expand All @@ -324,6 +330,10 @@ absl::StatusOr<SourcePtr> NewSourceImpl(std::string description, const T& text,
std::vector<char32_t> data32;
absl::InlinedVector<SourcePosition, 1> line_offsets;
while (index < text_size) {
if (offset >= max_codepoints) {
return absl::InvalidArgumentError(absl::StrCat(
"expression is larger than codepoint limit ", max_codepoints));
}
std::tie(code_point, code_units) = cel::internal::Utf8Decode(it);
if (ABSL_PREDICT_FALSE(code_point ==
cel::internal::kUnicodeReplacementCharacter &&
Expand Down Expand Up @@ -375,6 +385,10 @@ absl::StatusOr<SourcePtr> NewSourceImpl(std::string description, const T& text,
std::move(description), std::move(line_offsets), Traits::ToVector(text));
latin1:
while (index < text_size) {
if (offset >= max_codepoints) {
return absl::InvalidArgumentError(absl::StrCat(
"expression is larger than codepoint limit ", max_codepoints));
}
std::tie(code_point, code_units) = internal::Utf8Decode(it);
if (ABSL_PREDICT_FALSE(code_point ==
internal::kUnicodeReplacementCharacter &&
Expand Down Expand Up @@ -420,6 +434,10 @@ absl::StatusOr<SourcePtr> NewSourceImpl(std::string description, const T& text,
std::move(description), std::move(line_offsets), std::move(data8));
basic:
while (index < text_size) {
if (offset >= max_codepoints) {
return absl::InvalidArgumentError(absl::StrCat(
"expression is larger than codepoint limit ", max_codepoints));
}
std::tie(code_point, code_units) = internal::Utf8Decode(it);
if (ABSL_PREDICT_FALSE(code_point ==
internal::kUnicodeReplacementCharacter &&
Expand Down Expand Up @@ -453,6 +471,10 @@ absl::StatusOr<SourcePtr> NewSourceImpl(std::string description, const T& text,
std::move(description), std::move(line_offsets), std::move(data16));
supplemental:
while (index < text_size) {
if (offset >= max_codepoints) {
return absl::InvalidArgumentError(absl::StrCat(
"expression is larger than codepoint limit ", max_codepoints));
}
std::tie(code_point, code_units) = internal::Utf8Decode(it);
if (ABSL_PREDICT_FALSE(code_point ==
internal::kUnicodeReplacementCharacter &&
Expand Down Expand Up @@ -631,16 +653,27 @@ absl::Span<const SourcePosition> SourceSubrange::line_offsets() const {
return absl::MakeConstSpan(line_offsets_);
}

static size_t ClampLimit(int value) {
if (value < 0) {
return std::numeric_limits<size_t>::max();
}
return static_cast<size_t>(value);
}

absl::StatusOr<absl_nonnull SourcePtr> NewSource(absl::string_view content,
std::string description) {
std::string description,
const SourceOptions& options) {
return common_internal::NewSourceImpl(std::move(description), content,
content.size());
content.size(),
ClampLimit(options.max_codepoint_size));
}

absl::StatusOr<absl_nonnull SourcePtr> NewSource(const absl::Cord& content,
std::string description) {
std::string description,
const SourceOptions& options) {
return common_internal::NewSourceImpl(std::move(description), content,
content.size());
content.size(),
ClampLimit(options.max_codepoint_size));
}

} // namespace cel
37 changes: 33 additions & 4 deletions common/source.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,11 +221,40 @@ class SourceSubrange final : public Source {

using SourcePtr = std::unique_ptr<Source>;

absl::StatusOr<absl_nonnull SourcePtr> NewSource(
absl::string_view content, std::string description = "<input>");
struct SourceOptions {
// The maximum number of code points allowed in the source.
// A negative value indicates no limit (though still limited by
// int32_t max value).
int max_codepoint_size = 100'000;
};

absl::StatusOr<absl_nonnull SourcePtr> NewSource(absl::string_view content,
std::string description,
const SourceOptions& options);

absl::StatusOr<absl_nonnull SourcePtr> NewSource(const absl::Cord& content,
std::string description,
const SourceOptions& options);

inline absl::StatusOr<absl_nonnull SourcePtr> NewSource(
absl::string_view content, std::string description) {
return NewSource(content, std::move(description), SourceOptions{});
}

absl::StatusOr<absl_nonnull SourcePtr> NewSource(
const absl::Cord& content, std::string description = "<input>");
inline absl::StatusOr<absl_nonnull SourcePtr> NewSource(
const absl::Cord& content, std::string description) {
return NewSource(content, std::move(description), SourceOptions{});
}

inline absl::StatusOr<absl_nonnull SourcePtr> NewSource(
absl::string_view content) {
return NewSource(content, "<input>", SourceOptions{});
}

inline absl::StatusOr<absl_nonnull SourcePtr> NewSource(
const absl::Cord& content) {
return NewSource(content, "<input>", SourceOptions{});
}

} // namespace cel

Expand Down
51 changes: 50 additions & 1 deletion common/source_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@

#include "common/source.h"

#include <cstdint>
#include <optional>

#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "absl/types/optional.h"
#include "internal/testing.h"

namespace cel {
Expand Down Expand Up @@ -262,5 +265,51 @@ TEST(SourceSubrange, LineOffsetsMiddleSubrange) {
EXPECT_THAT(subrange.line_offsets(), ElementsAre(6, 10));
}

TEST(StringSource, CodepointLimitExceeded) {
SourceOptions options;
options.max_codepoint_size = 5;

EXPECT_THAT(
NewSource("123456", "test", options),
::absl_testing::StatusIs(
absl::StatusCode::kInvalidArgument,
::testing::HasSubstr("expression is larger than codepoint limit 5")));

ASSERT_OK_AND_ASSIGN(auto source, NewSource("12345", "test", options));
EXPECT_THAT(source->content().ToString(), ::testing::Eq("12345"));
}

TEST(StringSource, CodepointLimitMultibyteUtf8) {
SourceOptions options;
options.max_codepoint_size = 5;

// "Hello" consists of 5 full-width Unicode characters (15 bytes in
// UTF-8).
ASSERT_OK_AND_ASSIGN(auto source, NewSource("Hello", "test", options));
EXPECT_THAT(source->content().ToString(), ::testing::Eq("Hello"));

options.max_codepoint_size = 4;
EXPECT_THAT(
NewSource("Hello", "test", options),
::absl_testing::StatusIs(
absl::StatusCode::kInvalidArgument,
::testing::HasSubstr("expression is larger than codepoint limit 4")));
}

TEST(CordSource, CodepointLimitExceeded) {
SourceOptions options;
options.max_codepoint_size = 5;

EXPECT_THAT(
NewSource(absl::Cord("123456"), "test", options),
::absl_testing::StatusIs(
absl::StatusCode::kInvalidArgument,
::testing::HasSubstr("expression is larger than codepoint limit 5")));

ASSERT_OK_AND_ASSIGN(auto source,
NewSource(absl::Cord("12345"), "test", options));
EXPECT_THAT(source->content().ToString(), ::testing::Eq("12345"));
}

} // namespace
} // namespace cel
4 changes: 2 additions & 2 deletions compiler/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ class Compiler {
inline absl::StatusOr<ValidationResult> Compiler::Compile(
absl::string_view source, absl::string_view description,
google::protobuf::Arena* absl_nullable arena) const {
absl::StatusOr<SourcePtr> source_obj =
NewSource(source, std::string(description));
absl::StatusOr<std::unique_ptr<Source>> source_obj =
GetParser().PrepareSource(source, description);
if (!source_obj.ok()) {
return source_obj.status();
}
Expand Down
19 changes: 19 additions & 0 deletions compiler/compiler_factory_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -441,5 +441,24 @@ TEST(CompilerFactoryTest, CompileSourceOverload) {
EXPECT_TRUE(result.IsValid());
}

TEST(CompilerFactoryTest, CodepointLimitExceeded) {
CompilerOptions options;
options.parser_options.expression_size_codepoint_limit = 10;
ASSERT_OK_AND_ASSIGN(
auto builder,
NewCompilerBuilder(cel::internal::GetSharedTestingDescriptorPool(),
options));
ASSERT_OK_AND_ASSIGN(auto compiler, builder->Build());

EXPECT_THAT(
compiler->Compile("123456789012345", "test.cel"),
StatusIs(absl::StatusCode::kInvalidArgument,
HasSubstr("expression is larger than codepoint limit 10")));

ASSERT_OK_AND_ASSIGN(ValidationResult result,
compiler->Compile("1234567890", "test.cel"));
EXPECT_TRUE(result.IsValid());
}

} // namespace
} // namespace cel
8 changes: 8 additions & 0 deletions parser/internal/pratt_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParserImpl::ParseImpl(
return PrattParseImpl(source, macro_registry_, options_, parse_issues);
}

absl::StatusOr<std::unique_ptr<cel::Source>> PrattParserImpl::PrepareSourceImpl(
absl::string_view input, absl::string_view description) const {
return cel::NewSource(
input, std::string(description),
cel::SourceOptions{.max_codepoint_size =
options_.expression_size_codepoint_limit});
}

absl::StatusOr<std::unique_ptr<cel::Ast>> PrattParseImpl(
const cel::Source& source, const cel::MacroRegistry& registry,
const ParserOptions& options, std::vector<cel::ParseIssue>* parse_issues) {
Expand Down
3 changes: 3 additions & 0 deletions parser/internal/pratt_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ class PrattParserImpl final : public cel::Parser {
const cel::Source& source,
std::vector<cel::ParseIssue>* absl_nullable parse_issues) const override;

absl::StatusOr<std::unique_ptr<cel::Source>> PrepareSourceImpl(
absl::string_view input, absl::string_view description) const override;

std::unique_ptr<cel::ParserBuilder> ToBuilder() const override;

private:
Expand Down
8 changes: 8 additions & 0 deletions parser/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1781,6 +1781,14 @@ class ParserImpl : public cel::Parser {
std::move(parse_result.source_info));
}

absl::StatusOr<std::unique_ptr<cel::Source>> PrepareSourceImpl(
absl::string_view input, absl::string_view description) const override {
return cel::NewSource(
input, std::string(description),
cel::SourceOptions{.max_codepoint_size =
options_.expression_size_codepoint_limit});
}

std::unique_ptr<cel::ParserBuilder> ToBuilder() const override;

private:
Expand Down
21 changes: 21 additions & 0 deletions parser/parser_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,24 @@ class Parser {
absl::StatusOr<std::unique_ptr<cel::Ast>> Parse(
const cel::Source& source, std::vector<ParseIssue>* issues) const;

// Returns a Source object from the given input.
// Validates that the input is well-formed utf-8 and within the configured
// source limits.
absl::StatusOr<std::unique_ptr<cel::Source>> PrepareSource(
absl::string_view input, absl::string_view description) const;
absl::StatusOr<std::unique_ptr<cel::Source>> PrepareSource(
absl::string_view input) const;

// Returns a builder initialized with the configuration of this parser.
virtual std::unique_ptr<ParserBuilder> ToBuilder() const = 0;

protected:
virtual absl::StatusOr<std::unique_ptr<cel::Ast>> ParseImpl(
const cel::Source& source,
std::vector<ParseIssue>* absl_nullable parse_issues) const = 0;

virtual absl::StatusOr<std::unique_ptr<cel::Source>> PrepareSourceImpl(
absl::string_view input, absl::string_view description) const = 0;
};

inline absl::StatusOr<std::unique_ptr<cel::Ast>> Parser::Parse(
Expand All @@ -134,6 +145,16 @@ inline absl::StatusOr<std::unique_ptr<cel::Ast>> Parser::Parse(
return ParseImpl(source, issues);
}

inline absl::StatusOr<std::unique_ptr<cel::Source>> Parser::PrepareSource(
absl::string_view input, absl::string_view description) const {
return PrepareSourceImpl(input, description);
}

inline absl::StatusOr<std::unique_ptr<cel::Source>> Parser::PrepareSource(
absl::string_view input) const {
return PrepareSourceImpl(input, "<input>");
}

} // namespace cel

#endif // THIRD_PARTY_CEL_CPP_PARSER_PARSER_INTERFACE_H_
20 changes: 20 additions & 0 deletions parser/parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2189,6 +2189,26 @@ TEST_P(ParserTest, MacroExpansionNodeLimitNotExceeded) {
EXPECT_THAT(issues, testing::IsEmpty());
}

TEST_P(ParserTest, PrepareSourceForwardsCodepointLimit) {
auto builder = cel::NewParserBuilder(options_);
builder->GetOptions().expression_size_codepoint_limit = 10;
ASSERT_OK_AND_ASSIGN(auto parser, std::move(*builder).Build());

EXPECT_THAT(parser->PrepareSource("123456789012345", "test.cel"),
absl_testing::StatusIs(
absl::StatusCode::kInvalidArgument,
HasSubstr("expression is larger than codepoint limit 10")));

ASSERT_OK_AND_ASSIGN(auto source,
parser->PrepareSource("1234567890", "test.cel"));
EXPECT_EQ(source->description(), "test.cel");
EXPECT_EQ(source->content().ToString(), "1234567890");

ASSERT_OK_AND_ASSIGN(auto source_default_desc,
parser->PrepareSource("1234567890"));
EXPECT_EQ(source_default_desc->description(), "<input>");
}

std::string ExpressionTestName(
const testing::TestParamInfo<std::tuple<TestInfo, bool>>& test_info) {
const TestInfo& info = std::get<0>(test_info.param);
Expand Down
Loading