Skip to content

Commit 6dc8236

Browse files
authored
fix!: Raise AttributeError if an illegal attribute name is used
Signed-off-by: Daniel Azuma <[email protected]>
1 parent 3dd3153 commit 6dc8236

File tree

10 files changed

+74
-31
lines changed

10 files changed

+74
-31
lines changed

lib/cloud_events/event.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class << self
5858
# @param spec_version [String] The required `specversion` field.
5959
# @param kwargs [keywords] Additional parameters for the event.
6060
#
61-
def create spec_version:, **kwargs
61+
def create(spec_version:, **kwargs)
6262
case spec_version
6363
when "0.3"
6464
V0.new(spec_version: spec_version, **kwargs)

lib/cloud_events/event/field_interpreter.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ def initialize(args)
1414
@attributes = {}
1515
end
1616

17-
def finish_attributes
17+
def finish_attributes(requires_lc_start: false)
1818
@args.each do |key, value|
19+
check_attribute_name(key, requires_lc_start)
1920
@attributes[key.freeze] = value.to_s.freeze unless value.nil?
2021
end
2122
@args = {}
@@ -131,6 +132,13 @@ def object(keys, required: false, allow_nil: false)
131132
@attributes[keys.first.freeze] = raw
132133
converted
133134
end
135+
136+
def check_attribute_name(key, requires_lc_start)
137+
regex = requires_lc_start ? /^[a-z][a-z0-9]*$/ : /^[a-z0-9]+$/
138+
unless regex.match?(key)
139+
raise(AttributeError, "Illegal key: #{key.inspect} must consist only of digits and lower-case letters")
140+
end
141+
end
134142
end
135143
end
136144
end

lib/cloud_events/event/v0.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class V0
7575
# (Also available using the deprecated keyword `attributes`.)
7676
# @param args [keywords] The data and attributes, as keyword arguments.
7777
#
78-
def initialize set_attributes: nil, attributes: nil, **args
78+
def initialize(set_attributes: nil, attributes: nil, **args)
7979
interpreter = FieldInterpreter.new(set_attributes || attributes || args)
8080
@spec_version = interpreter.spec_version(["specversion", "spec_version"], accept: /^0\.3$/)
8181
@id = interpreter.string(["id"], required: true)
@@ -88,7 +88,7 @@ def initialize set_attributes: nil, attributes: nil, **args
8888
@schema_url = interpreter.uri(["schemaurl", "schema_url"])
8989
@subject = interpreter.string(["subject"])
9090
@time = interpreter.rfc3339_date_time(["time"])
91-
@attributes = interpreter.finish_attributes
91+
@attributes = interpreter.finish_attributes(requires_lc_start: true)
9292
freeze
9393
end
9494

@@ -101,7 +101,7 @@ def initialize set_attributes: nil, attributes: nil, **args
101101
# @param changes [keywords] See {#initialize} for a list of arguments.
102102
# @return [FunctionFramework::CloudEvents::Event]
103103
#
104-
def with **changes
104+
def with(**changes)
105105
attributes = @attributes.merge(changes)
106106
V0.new(set_attributes: attributes)
107107
end

lib/cloud_events/event/v1.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class V1
136136
# (Also available using the deprecated keyword `attributes`.)
137137
# @param args [keywords] The data and attributes, as keyword arguments.
138138
#
139-
def initialize set_attributes: nil, attributes: nil, **args
139+
def initialize(set_attributes: nil, attributes: nil, **args)
140140
interpreter = FieldInterpreter.new(set_attributes || attributes || args)
141141
@spec_version = interpreter.spec_version(["specversion", "spec_version"], accept: /^1(\.|$)/)
142142
@id = interpreter.string(["id"], required: true)
@@ -167,7 +167,7 @@ def initialize set_attributes: nil, attributes: nil, **args
167167
# @param changes [keywords] See {#initialize} for a list of arguments.
168168
# @return [FunctionFramework::CloudEvents::Event]
169169
#
170-
def with **changes
170+
def with(**changes)
171171
changes = Utils.keys_to_strings(changes)
172172
attributes = @attributes.dup
173173
if changes.key?("data") || changes.key?("data_encoded")

lib/cloud_events/format.rb

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ module Format
7979
# @return [Hash] if accepting the request and returning a result
8080
# @return [nil] if declining the request.
8181
#
82-
def decode_event **_kwargs
82+
def decode_event(**_kwargs)
8383
nil
8484
end
8585

@@ -116,7 +116,7 @@ def decode_event **_kwargs
116116
# @return [Hash] if accepting the request and returning a result
117117
# @return [nil] if declining the request.
118118
#
119-
def encode_event **_kwargs
119+
def encode_event(**_kwargs)
120120
nil
121121
end
122122

@@ -155,7 +155,7 @@ def encode_event **_kwargs
155155
# @return [Hash] if accepting the request and returning a result
156156
# @return [nil] if declining the request.
157157
#
158-
def decode_data **_kwargs
158+
def decode_data(**_kwargs)
159159
nil
160160
end
161161

@@ -194,7 +194,7 @@ def decode_data **_kwargs
194194
# @return [Hash] if accepting the request and returning a result
195195
# @return [nil] if declining the request.
196196
#
197-
def encode_data **_kwargs
197+
def encode_data(**_kwargs)
198198
nil
199199
end
200200

@@ -227,7 +227,7 @@ def initialize(formats = [], &result_checker)
227227
##
228228
# Implements {Format#decode_event}
229229
#
230-
def decode_event **kwargs
230+
def decode_event(**kwargs)
231231
@formats.each do |elem|
232232
result = elem.decode_event(**kwargs)
233233
result = @result_checker.call(result) if @result_checker
@@ -239,7 +239,7 @@ def decode_event **kwargs
239239
##
240240
# Implements {Format#encode_event}
241241
#
242-
def encode_event **kwargs
242+
def encode_event(**kwargs)
243243
@formats.each do |elem|
244244
result = elem.encode_event(**kwargs)
245245
result = @result_checker.call(result) if @result_checker
@@ -251,7 +251,7 @@ def encode_event **kwargs
251251
##
252252
# Implements {Format#decode_data}
253253
#
254-
def decode_data **kwargs
254+
def decode_data(**kwargs)
255255
@formats.each do |elem|
256256
result = elem.decode_data(**kwargs)
257257
result = @result_checker.call(result) if @result_checker
@@ -263,7 +263,7 @@ def decode_data **kwargs
263263
##
264264
# Implements {Format#encode_data}
265265
#
266-
def encode_data **kwargs
266+
def encode_data(**kwargs)
267267
@formats.each do |elem|
268268
result = elem.encode_data(**kwargs)
269269
result = @result_checker.call(result) if @result_checker

lib/cloud_events/http_binding.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def probable_event?(env)
166166
# @raise [CloudEvents::CloudEventsError] if an event could not be decoded
167167
# from the request.
168168
#
169-
def decode_event env, allow_opaque: false, **format_args
169+
def decode_event(env, allow_opaque: false, **format_args)
170170
request_method = env["REQUEST_METHOD"]
171171
raise(NotCloudEventError, "Request method is #{request_method}") if ILLEGAL_METHODS.include?(request_method)
172172
content_type_string = env["CONTENT_TYPE"]
@@ -204,7 +204,7 @@ def decode_event env, allow_opaque: false, **format_args
204204
# @param format_args [keywords] Extra args to pass to the formatter.
205205
# @return [Array(headers,String)]
206206
#
207-
def encode_event event, structured_format: false, **format_args
207+
def encode_event(event, structured_format: false, **format_args)
208208
if event.is_a?(Event::Opaque)
209209
[{ "Content-Type" => event.content_type.to_s }, event.content]
210210
elsif !structured_format
@@ -243,7 +243,7 @@ def encode_event event, structured_format: false, **format_args
243243
# @raise [CloudEvents::CloudEventsError] if the request appears to be a
244244
# CloudEvent but decoding failed.
245245
#
246-
def decode_rack_env env, **format_args
246+
def decode_rack_env(env, **format_args)
247247
content_type_string = env["CONTENT_TYPE"]
248248
content_type = ContentType.new(content_type_string) if content_type_string
249249
content = read_with_charset(env["rack.input"], content_type&.charset)
@@ -262,7 +262,7 @@ def decode_rack_env env, **format_args
262262
# @param format_args [keywords] Extra args to pass to the formatter.
263263
# @return [Array(headers,String)]
264264
#
265-
def encode_structured_content event, format_name, **format_args
265+
def encode_structured_content(event, format_name, **format_args)
266266
result = @event_encoders[format_name]&.encode_event(event: event,
267267
data_encoder: @data_encoders,
268268
**format_args)
@@ -280,7 +280,7 @@ def encode_structured_content event, format_name, **format_args
280280
# @param format_args [keywords] Extra args to pass to the formatter.
281281
# @return [Array(headers,String)]
282282
#
283-
def encode_batched_content event_batch, format_name, **format_args
283+
def encode_batched_content(event_batch, format_name, **format_args)
284284
result = @event_encoders[format_name]&.encode_event(event_batch: event_batch,
285285
data_encoder: @data_encoders,
286286
**format_args)
@@ -297,7 +297,7 @@ def encode_batched_content event_batch, format_name, **format_args
297297
# @param format_args [keywords] Extra args to pass to the formatter.
298298
# @return [Array(headers,String)]
299299
#
300-
def encode_binary_content event, legacy_data_encode: true, **format_args
300+
def encode_binary_content(event, legacy_data_encode: true, **format_args)
301301
headers = {}
302302
event.to_h.each do |key, value|
303303
unless ["data", "data_encoded", "datacontenttype"].include?(key)
@@ -369,7 +369,7 @@ def add_named_formatter(collection, formatter, name)
369369
# Decode a single event from the given request body and content type in
370370
# structured mode.
371371
#
372-
def decode_structured_content content, content_type, allow_opaque, **format_args
372+
def decode_structured_content(content, content_type, allow_opaque, **format_args)
373373
result = @event_decoders.decode_event(content: content,
374374
content_type: content_type,
375375
data_decoder: @data_decoders,
@@ -389,7 +389,7 @@ def decode_structured_content content, content_type, allow_opaque, **format_args
389389
# TODO: legacy_data_decode is deprecated and can be removed when
390390
# decode_rack_env is removed.
391391
#
392-
def decode_binary_content content, content_type, env, legacy_data_decode, **format_args
392+
def decode_binary_content(content, content_type, env, legacy_data_decode, **format_args)
393393
spec_version = env["HTTP_CE_SPECVERSION"]
394394
return nil unless spec_version
395395
unless spec_version =~ /^0\.3|1(\.|$)/
@@ -479,13 +479,13 @@ def read_with_charset(io, charset)
479479
# @private
480480
module DefaultDataFormat
481481
# @private
482-
def self.decode_data content: nil, content_type: nil, **_extra_kwargs
482+
def self.decode_data(content: nil, content_type: nil, **_extra_kwargs)
483483
return nil unless content_type.nil?
484484
{ data: content, content_type: nil }
485485
end
486486

487487
# @private
488-
def self.encode_data data: nil, content_type: nil, **_extra_kwargs
488+
def self.encode_data(data: nil, content_type: nil, **_extra_kwargs)
489489
return nil unless content_type.nil?
490490
{ content: data.to_s, content_type: nil }
491491
end

lib/cloud_events/json_format.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class JsonFormat
3737
# @raise [CloudEvents::SpecVersionError] if an unsupported specversion is
3838
# found.
3939
#
40-
def decode_event content: nil, content_type: nil, data_decoder: nil, **_other_kwargs
40+
def decode_event(content: nil, content_type: nil, data_decoder: nil, **_other_kwargs)
4141
return nil unless content && content_type&.media_type == "application" && content_type&.subtype_format == "json"
4242
case content_type.subtype_base
4343
when "cloudevents"
@@ -77,7 +77,7 @@ def decode_event content: nil, content_type: nil, data_decoder: nil, **_other_kw
7777
# @return [nil] if declining the request.
7878
# @raise [CloudEvents::FormatSyntaxError] if the JSON could not be parsed
7979
#
80-
def encode_event event: nil, event_batch: nil, data_encoder: nil, sort: false, **_other_kwargs
80+
def encode_event(event: nil, event_batch: nil, data_encoder: nil, sort: false, **_other_kwargs)
8181
if event && !event_batch
8282
structure = encode_hash_structure(event, data_encoder: data_encoder)
8383
structure = sort_keys(structure) if sort
@@ -119,7 +119,7 @@ def encode_event event: nil, event_batch: nil, data_encoder: nil, sort: false, *
119119
# @raise [CloudEvents::SpecVersionError] if an unsupported specversion is
120120
# found.
121121
#
122-
def decode_data spec_version: nil, content: nil, content_type: nil, **_other_kwargs
122+
def decode_data(spec_version: nil, content: nil, content_type: nil, **_other_kwargs)
123123
return nil unless spec_version
124124
return nil unless content
125125
return nil unless json_content_type?(content_type)
@@ -154,7 +154,7 @@ def decode_data spec_version: nil, content: nil, content_type: nil, **_other_kwa
154154
# @return [Hash] if accepting the request.
155155
# @return [nil] if declining the request.
156156
#
157-
def encode_data spec_version: nil, data: UNSPECIFIED, content_type: nil, sort: false, **_other_kwargs
157+
def encode_data(spec_version: nil, data: UNSPECIFIED, content_type: nil, sort: false, **_other_kwargs)
158158
return nil unless spec_version
159159
return nil if data == UNSPECIFIED
160160
return nil unless json_content_type?(content_type)

lib/cloud_events/text_format.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TextFormat
3030
# @return [Hash] if accepting the request.
3131
# @return [nil] if declining the request.
3232
#
33-
def decode_data content: nil, content_type: nil, **_other_kwargs
33+
def decode_data(content: nil, content_type: nil, **_other_kwargs)
3434
return nil unless content
3535
return nil unless text_content_type?(content_type)
3636
{ data: content.to_s, content_type: content_type }
@@ -56,7 +56,7 @@ def decode_data content: nil, content_type: nil, **_other_kwargs
5656
# @return [Hash] if accepting the request.
5757
# @return [nil] if declining the request.
5858
#
59-
def encode_data data: UNSPECIFIED, content_type: nil, **_other_kwargs
59+
def encode_data(data: UNSPECIFIED, content_type: nil, **_other_kwargs)
6060
return nil if data == UNSPECIFIED
6161
return nil unless text_content_type?(content_type)
6262
{ content: data.to_s, content_type: content_type }

test/event/test_v0.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,25 @@
211211
assert_equal "The type field is required", error.message
212212
end
213213

214+
it "validates attribute name" do
215+
error = assert_raises(CloudEvents::AttributeError) do
216+
CloudEvents::Event::V0.new(id: my_id,
217+
source: my_source,
218+
type: my_type,
219+
spec_version: spec_version,
220+
"1parent": my_trace_parent)
221+
end
222+
assert_includes error.message, "Illegal key: \"1parent\""
223+
error = assert_raises(CloudEvents::AttributeError) do
224+
CloudEvents::Event::V0.new(id: my_id,
225+
source: my_source,
226+
type: my_type,
227+
spec_version: spec_version,
228+
trace_parent: my_trace_parent)
229+
end
230+
assert_includes error.message, "Illegal key: \"trace_parent\""
231+
end
232+
214233
it "handles extension attributes" do
215234
event = CloudEvents::Event::V0.new(id: my_id,
216235
source: my_source,

test/event/test_v1.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,22 @@
272272
assert_equal "The type field is required", error.message
273273
end
274274

275+
it "validates attribute name" do
276+
CloudEvents::Event::V1.new(id: my_id,
277+
source: my_source,
278+
type: my_type,
279+
spec_version: spec_version,
280+
"1parent": my_trace_parent)
281+
error = assert_raises(CloudEvents::AttributeError) do
282+
CloudEvents::Event::V1.new(id: my_id,
283+
source: my_source,
284+
type: my_type,
285+
spec_version: spec_version,
286+
trace_parent: my_trace_parent)
287+
end
288+
assert_includes error.message, "Illegal key: \"trace_parent\""
289+
end
290+
275291
it "handles extension attributes" do
276292
event = CloudEvents::Event::V1.new(id: my_id,
277293
source: my_source,

0 commit comments

Comments
 (0)