3
3
require "openssl"
4
4
require "time"
5
5
require_relative "base"
6
+ require_relative "timestamp_validator"
6
7
7
8
module Hooks
8
9
module Plugins
@@ -190,7 +191,6 @@ def self.normalize_headers(headers)
190
191
# @return [Boolean] true if timestamp is valid or not required, false otherwise
191
192
# @note Returns false if timestamp header is missing when required
192
193
# @note Tolerance is applied as absolute difference (past or future)
193
- # @note Tries ISO 8601 UTC format first, then falls back to Unix timestamp
194
194
# @api private
195
195
def self . valid_timestamp? ( headers , config )
196
196
timestamp_header = config [ :timestamp_header ]
@@ -199,87 +199,16 @@ def self.valid_timestamp?(headers, config)
199
199
200
200
timestamp_value = headers [ timestamp_header . downcase ]
201
201
return false unless timestamp_value
202
- return false if timestamp_value . strip . empty?
203
202
204
- parsed_timestamp = parse_timestamp ( timestamp_value . strip )
205
- return false unless parsed_timestamp . is_a? ( Integer )
206
-
207
- now = Time . now . utc . to_i
208
- ( now - parsed_timestamp ) . abs <= tolerance
209
- end
210
-
211
- # Parse timestamp value supporting both ISO 8601 UTC and Unix formats
212
- #
213
- # @param timestamp_value [String] The timestamp string to parse
214
- # @return [Integer, nil] Epoch seconds if parsing succeeds, nil otherwise
215
- # @note Security: Strict validation prevents various injection attacks
216
- # @api private
217
- def self . parse_timestamp ( timestamp_value )
218
- # Reject if contains any control characters, whitespace, or null bytes
219
- if timestamp_value =~ /[\u0000 -\u001F \u007F -\u009F ]/
220
- log . warn ( "Auth::HMAC validation failed: Timestamp contains invalid characters" )
221
- return nil
222
- end
223
- ts = parse_iso8601_timestamp ( timestamp_value )
224
- return ts if ts
225
- ts = parse_unix_timestamp ( timestamp_value )
226
- return ts if ts
227
-
228
- # If neither format matches, return nil
229
- log . warn ( "Auth::HMAC validation failed: Timestamp (#{ timestamp_value } ) is not valid ISO 8601 UTC or Unix format" )
230
- return nil
231
- end
232
-
233
- # Check if timestamp string looks like ISO 8601 UTC format (must have UTC indicator)
234
- #
235
- # @param timestamp_value [String] The timestamp string to check
236
- # @return [Boolean] true if it appears to be ISO 8601 format (with or without UTC indicator)
237
- # @api private
238
- def self . iso8601_timestamp? ( timestamp_value )
239
- !!( timestamp_value =~ /\A \d {4}-\d {2}-\d {2}[T ]\d {2}:\d {2}:\d {2}(?:\. \d +)?(Z|\+ 00:00|\+ 0000)?\z / )
240
- end
241
-
242
- # Parse ISO 8601 UTC timestamp string (must have UTC indicator)
243
- #
244
- # @param timestamp_value [String] ISO 8601 timestamp string
245
- # @return [Integer, nil] Epoch seconds if parsing succeeds, nil otherwise
246
- # @note Only accepts UTC timestamps (ending with 'Z', '+00:00', '+0000')
247
- # @api private
248
- def self . parse_iso8601_timestamp ( timestamp_value )
249
- if timestamp_value =~ /\A (\d {4}-\d {2}-\d {2}) (\d {2}:\d {2}:\d {2}(?:\. \d +)?)(?: )\+ 0000\z /
250
- timestamp_value = "#{ $1} T#{ $2} +00:00"
251
- end
252
- # Ensure the timestamp explicitly includes a UTC indicator
253
- return nil unless timestamp_value =~ /(Z|\+ 00:00|\+ 0000)\z /
254
- return nil unless iso8601_timestamp? ( timestamp_value )
255
- t = Time . parse ( timestamp_value ) rescue nil
256
- return nil unless t
257
- # The check for UTC indicator in regex makes this t.utc? or t.utc_offset == 0 redundant
258
- # but kept for safety, though it should always be true now if Time.parse succeeds.
259
- ( t . utc? || t . utc_offset == 0 ) ? t . to_i : nil
260
- end
261
-
262
- # Parse Unix timestamp string (must be positive integer, no leading zeros except for "0")
263
- #
264
- # @param timestamp_value [String] Unix timestamp string
265
- # @return [Integer, nil] Epoch seconds if parsing succeeds, nil otherwise
266
- # @note Only accepts positive integer values, no leading zeros except for "0"
267
- # @api private
268
- def self . parse_unix_timestamp ( timestamp_value )
269
- return nil unless unix_timestamp? ( timestamp_value )
270
- ts = timestamp_value . to_i
271
- return nil if ts <= 0
272
- ts
203
+ timestamp_validator . valid? ( timestamp_value , tolerance )
273
204
end
274
205
275
- # Check if timestamp string looks like Unix timestamp format (no leading zeros except "0")
206
+ # Get timestamp validator instance
276
207
#
277
- # @param timestamp_value [String] The timestamp string to check
278
- # @return [Boolean] true if it appears to be Unix timestamp format
208
+ # @return [TimestampValidator] Singleton timestamp validator instance
279
209
# @api private
280
- def self . unix_timestamp? ( timestamp_value )
281
- return true if timestamp_value == "0"
282
- !!( timestamp_value =~ /\A [1-9]\d *\z / )
210
+ def self . timestamp_validator
211
+ @timestamp_validator ||= TimestampValidator . new
283
212
end
284
213
285
214
# Compute HMAC signature based on configuration requirements
0 commit comments