jcrvalidator 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -25,13 +25,20 @@ require 'jcr/check_groups'
25
25
 
26
26
  module JCR
27
27
 
28
- def self.evaluate_value_rule jcr, rule_atom, data, mapping
28
+ def self.evaluate_value_rule jcr, rule_atom, data, econs
29
+
30
+ push_trace_stack( econs, jcr )
31
+ trace( econs, "Evaluating value rule starting at #{slice_to_s(jcr)}" )
32
+ trace_def( econs, "value", jcr, data )
29
33
  rules, annotations = get_rules_and_annotations( jcr )
30
34
 
31
- return evaluate_reject( annotations, evaluate_values( rules[0], rule_atom, data, mapping ) )
35
+ retval = evaluate_not( annotations, evaluate_values( rules[0], rule_atom, data, econs ), econs )
36
+ trace_eval( econs, "Value", retval)
37
+ pop_trace_stack( econs )
38
+ return retval
32
39
  end
33
40
 
34
- def self.evaluate_values jcr, rule_atom, data, mapping
41
+ def self.evaluate_values jcr, rule_atom, data, econs
35
42
  case
36
43
 
37
44
  #
@@ -53,12 +60,34 @@ module JCR
53
60
  when jcr[:integer]
54
61
  i = jcr[:integer].to_s.to_i
55
62
  return bad_value( jcr, rule_atom, i, data ) unless data == i
63
+ when jcr[:integer_min] != nil && jcr[:integer_max] == nil
64
+ return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
65
+ min = jcr[:integer_min].to_s.to_i
66
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
67
+ when jcr[:integer_max] == nil && jcr[:integer_max] != nil
68
+ return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
69
+ max = jcr[:integer_max].to_s.to_i
70
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
56
71
  when jcr[:integer_min],jcr[:integer_max]
57
72
  return bad_value( jcr, rule_atom, "integer", data ) unless data.is_a?( Fixnum )
58
73
  min = jcr[:integer_min].to_s.to_i
59
74
  return bad_value( jcr, rule_atom, min, data ) unless data >= min
60
75
  max = jcr[:integer_max].to_s.to_i
61
76
  return bad_value( jcr, rule_atom, max, data ) unless data <= max
77
+ when jcr[:sized_int_v]
78
+ bits = jcr[:sized_int_v][:bits].to_i
79
+ return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Fixnum )
80
+ min = -(2**(bits-1))
81
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
82
+ max = 2**(bits-1)-1
83
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
84
+ when jcr[:sized_uint_v]
85
+ bits = jcr[:sized_uint_v][:bits].to_i
86
+ return bad_value( jcr, rule_atom, "int" + bits.to_s, data ) unless data.is_a?( Fixnum )
87
+ min = 0
88
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
89
+ max = 2**bits-1
90
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
62
91
 
63
92
  #
64
93
  # floats
@@ -72,12 +101,25 @@ module JCR
72
101
  when jcr[:float]
73
102
  f = jcr[:float].to_s.to_f
74
103
  return bad_value( jcr, rule_atom, f, data ) unless data == f
104
+ when jcr[:float_min] != nil && jcr[:float_max] == nil
105
+ return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
106
+ min = jcr[:float_min].to_s.to_f
107
+ return bad_value( jcr, rule_atom, min, data ) unless data >= min
108
+ when jcr[:float_min] == nil && jcr[:float_max] != nil
109
+ return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
110
+ max = jcr[:float_max].to_s.to_f
111
+ return bad_value( jcr, rule_atom, max, data ) unless data <= max
75
112
  when jcr[:float_min],jcr[:float_max]
76
113
  return bad_value( jcr, rule_atom, "float", data ) unless data.is_a?( Float )
77
114
  min = jcr[:float_min].to_s.to_f
78
115
  return bad_value( jcr, rule_atom, min, data ) unless data >= min
79
116
  max = jcr[:float_max].to_s.to_f
80
117
  return bad_value( jcr, rule_atom, max, data ) unless data <= max
118
+ when jcr[:double_v]
119
+ sf = jcr[:double_v].to_s
120
+ if sf == "double"
121
+ return bad_value( jcr, rule_atom, "double", data ) unless data.is_a?( Float )
122
+ end
81
123
 
82
124
  #
83
125
  # boolean
@@ -113,7 +155,7 @@ module JCR
113
155
  # ip addresses
114
156
  #
115
157
 
116
- when jcr[:ip4]
158
+ when jcr[:ipv4]
117
159
  return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless data.is_a? String
118
160
  begin
119
161
  ip = IPAddr.new( data )
@@ -121,7 +163,7 @@ module JCR
121
163
  return bad_value( jcr, rule_atom, "IPv4 Address", data )
122
164
  end
123
165
  return bad_value( jcr, rule_atom, "IPv4 Address", data ) unless ip.ipv4?
124
- when jcr[:ip6]
166
+ when jcr[:ipv6]
125
167
  return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless data.is_a? String
126
168
  begin
127
169
  ip = IPAddr.new( data )
@@ -129,6 +171,14 @@ module JCR
129
171
  return bad_value( jcr, rule_atom, "IPv6 Address", data )
130
172
  end
131
173
  return bad_value( jcr, rule_atom, "IPv6 Address", data ) unless ip.ipv6?
174
+ when jcr[:ipaddr]
175
+ return bad_value( jcr, rule_atom, "IP Address", data ) unless data.is_a? String
176
+ begin
177
+ ip = IPAddr.new( data )
178
+ rescue IPAddr::InvalidAddressError
179
+ return bad_value( jcr, rule_atom, "IP Address", data )
180
+ end
181
+ return bad_value( jcr, rule_atom, "IP Address", data ) unless ip.ipv6? || ip.ipv4?
132
182
 
133
183
  #
134
184
  # domain names
@@ -202,25 +252,109 @@ module JCR
202
252
  p = BigPhoney::PhoneNumber.new( data )
203
253
  return bad_value( jcr, rule_atom, "Phone Number", data ) unless p.valid?
204
254
 
255
+ #
256
+ # hex values
257
+ #
258
+
259
+ when jcr[:hex]
260
+ return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.is_a? String
261
+ return bad_value( jcr, rule_atom, "Hex Data", data ) unless data.length % 2 == 0
262
+ pad_start = false
263
+ data.each_char do |char|
264
+ unless (char >= '0' && char <='9') \
265
+ || (char >= 'A' && char <= 'F') \
266
+ || (char >= 'a' && char <= 'f')
267
+ return bad_value( jcr, rule_atom, "Hex Data", data )
268
+ end
269
+ end
270
+
271
+ #
272
+ # base32hex values
273
+ #
274
+
275
+ when jcr[:base32hex]
276
+ return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.is_a? String
277
+ return bad_value( jcr, rule_atom, "Base32hex Data", data ) unless data.length % 8 == 0
278
+ pad_start = false
279
+ data.each_char do |char|
280
+ if char == '='
281
+ pad_start = true
282
+ elsif pad_start && char != '='
283
+ return bad_value( jcr, rule_atom, "Base32hex Data", data )
284
+ else
285
+ unless (char >= '0' && char <='9') \
286
+ || (char >= 'A' && char <= 'V') \
287
+ || (char >= 'a' && char <= 'v')
288
+ return bad_value( jcr, rule_atom, "Base32hex Data", data )
289
+ end
290
+ end
291
+ end
292
+
293
+ #
294
+ # base32 values
295
+ #
296
+
297
+ when jcr[:base32]
298
+ return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.is_a? String
299
+ return bad_value( jcr, rule_atom, "Base 32 Data", data ) unless data.length % 8 == 0
300
+ pad_start = false
301
+ data.each_char do |char|
302
+ if char == '='
303
+ pad_start = true
304
+ elsif pad_start && char != '='
305
+ return bad_value( jcr, rule_atom, "Base 32 Data", data )
306
+ else
307
+ unless (char >= 'a' && char <= 'z') \
308
+ || (char >= 'A' && char <= 'Z') \
309
+ || (char >= '2' && char <='7')
310
+ return bad_value( jcr, rule_atom, "Base 32 Data", data )
311
+ end
312
+ end
313
+ end
314
+
315
+ #
316
+ # base64url values
317
+ #
318
+
319
+ when jcr[:base64url]
320
+ return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.is_a? String
321
+ return bad_value( jcr, rule_atom, "Base64url Data", data ) unless data.length % 4 == 0
322
+ pad_start = false
323
+ data.each_char do |char|
324
+ if char == '='
325
+ pad_start = true
326
+ elsif pad_start && char != '='
327
+ return bad_value( jcr, rule_atom, "Base64url Data", data )
328
+ else
329
+ unless (char >= 'a' && char <= 'z') \
330
+ || (char >= 'A' && char <= 'Z') \
331
+ || (char >= '0' && char <='9') \
332
+ || char == '-' || char == '_'
333
+ return bad_value( jcr, rule_atom, "Base64url Data", data )
334
+ end
335
+ end
336
+ end
337
+
205
338
  #
206
339
  # base64 values
207
340
  #
208
341
 
209
342
  when jcr[:base64]
210
343
  return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.is_a? String
211
- return bad_value( jcr, rule_atom, "Base 64 Data", data ) if data.empty?
344
+ return bad_value( jcr, rule_atom, "Base 64 Data", data ) unless data.length % 4 == 0
212
345
  pad_start = false
213
346
  data.each_char do |char|
214
- if pad_start && char != '='
215
- return bad_value( jcr, rule_atom, "Base 64 Data", data )
216
- elsif char == '='
347
+ if char == '='
217
348
  pad_start = true
218
- end
219
- unless (char >= 'a' && char <= 'z') \
220
- || (char >= 'A' && char <= 'Z') \
221
- || (char >= '0' && char <='9') \
222
- || char == '=' || char == '+' || char == '/'
349
+ elsif pad_start && char != '='
223
350
  return bad_value( jcr, rule_atom, "Base 64 Data", data )
351
+ else
352
+ unless (char >= 'a' && char <= 'z') \
353
+ || (char >= 'A' && char <= 'Z') \
354
+ || (char >= '0' && char <='9') \
355
+ || char == '+' || char == '/'
356
+ return bad_value( jcr, rule_atom, "Base 64 Data", data )
357
+ end
224
358
  end
225
359
  end
226
360
 
@@ -228,14 +362,14 @@ module JCR
228
362
  # time and date values
229
363
  #
230
364
 
231
- when jcr[:date_time]
365
+ when jcr[:datetime]
232
366
  return bad_value( jcr, rule_atom, "Time and Date", data ) unless data.is_a? String
233
367
  begin
234
368
  Time.iso8601( data )
235
369
  rescue ArgumentError
236
370
  return bad_value( jcr, rule_atom, "Time and Date", data )
237
371
  end
238
- when jcr[:full_date]
372
+ when jcr[:date]
239
373
  return bad_value( jcr, rule_atom, "Date", data ) unless data.is_a? String
240
374
  begin
241
375
  d = data + "T23:20:50.52Z"
@@ -243,7 +377,7 @@ module JCR
243
377
  rescue ArgumentError
244
378
  return bad_value( jcr, rule_atom, "Date", data )
245
379
  end
246
- when jcr[:full_time]
380
+ when jcr[:time]
247
381
  return bad_value( jcr, rule_atom, "Time", data ) unless data.is_a? String
248
382
  begin
249
383
  t = "1985-04-12T" + data + "Z"
@@ -264,7 +398,7 @@ module JCR
264
398
  #
265
399
 
266
400
  when jcr[:group_rule]
267
- return evaluate_group_rule jcr[:group_rule], rule_atom, data, mapping
401
+ return evaluate_group_rule jcr[:group_rule], rule_atom, data, econs
268
402
 
269
403
  else
270
404
  raise "unknown value rule evaluation. this shouldn't happen"
@@ -273,7 +407,110 @@ module JCR
273
407
  end
274
408
 
275
409
  def self.bad_value jcr, rule_atom, expected, actual
276
- Evaluation.new( false, "expected #{expected} but got #{actual} at #{jcr} from #{rule_atom}" )
410
+ Evaluation.new( false, "expected #{expected} but got #{actual} for #{raised_rule(jcr,rule_atom)}" )
411
+ end
412
+
413
+ def self.value_to_s( jcr, shallow=true )
414
+
415
+ rules, annotations = get_rules_and_annotations( jcr )
416
+
417
+ rule = rules[ 0 ]
418
+ retval = ""
419
+ case
420
+
421
+ when rule[:any]
422
+ retval = "any"
423
+
424
+ when rule[:integer_v]
425
+ retval = rule[:integer_v].to_s
426
+ when rule[:integer]
427
+ retval = rule[:integer].to_s.to_i
428
+ when rule[:integer_min],rule[:integer_max]
429
+ min = "-INF"
430
+ max = "INF"
431
+ min = rule[:integer_min].to_s.to_i if rule[:integer_min]
432
+ max = rule[:integer_max].to_s.to_i if rule[:integer_max]
433
+ retval = "#{min}..#{max}"
434
+ when rule[:sized_int_v]
435
+ retval = "int" + rule[:sized_int_v][:bits].to_s
436
+ when rule[:sized_uint_v]
437
+ retval = "uint" + rule[:sized_uint_v][:bits].to_s
438
+
439
+ when rule[:double_v]
440
+ retval = rule[:double_v].to_s
441
+ when rule[:float_v]
442
+ retval = rule[:float_v].to_s
443
+ when rule[:float]
444
+ retval = rule[:float].to_s.to_f
445
+ when rule[:float_min],rule[:float_max]
446
+ min = "-INF"
447
+ max = "INF"
448
+ min = rule[:float_min].to_s.to_f if rule[:float_min]
449
+ max = rule[:float_max].to_s.to_f if rule[:float_max]
450
+ retval = "#{min}..#{max}"
451
+
452
+ when rule[:true_v]
453
+ retval = "true"
454
+ when rule[:false_v]
455
+ retval = "false"
456
+ when rule[:boolean_v]
457
+ retval = "boolean"
458
+
459
+ when rule[:string]
460
+ retval = "string"
461
+ when rule[:q_string]
462
+ retval = %Q|"#{rule[:q_string].to_s}"|
463
+
464
+ when rule[:regex]
465
+ retval = "/#{rule[:regex].to_s}/"
466
+
467
+ when rule[:ipv4]
468
+ retval = "ipv4"
469
+ when rule[:ipv6]
470
+ retval = "ipv6"
471
+
472
+ when rule[:fqdn]
473
+ retval = "fqdn"
474
+ when rule[:idn]
475
+ retval = "idn"
476
+
477
+ when rule[:uri]
478
+ retval = "URI"
479
+ when rule[:uri_template]
480
+ retval = "URI template #{rule[:uri_template].to_s}"
481
+
482
+ when rule[:email]
483
+ retval = "email"
484
+
485
+ when rule[:phone]
486
+ retval = "phone"
487
+
488
+ when rule[:hex]
489
+ retval = "hex"
490
+ when rule[:base32url]
491
+ retval = "base32url"
492
+ when rule[:base64url]
493
+ retval = "base64url"
494
+ when rule[:base64]
495
+ retval = "base64"
496
+
497
+ when rule[:datetime]
498
+ retval = "datetime"
499
+ when rule[:date]
500
+ retval = "date"
501
+ when rule[:time]
502
+ retval = "time"
503
+
504
+ when rule[:null]
505
+ retval = "null"
506
+
507
+ when rule[:group_rule]
508
+ retval = group_to_s( rule[:group_rule], shallow )
509
+
510
+ else
511
+ retval = "** unknown value rule **"
512
+ end
513
+ return annotations_to_s( annotations ) + retval.to_s
277
514
  end
278
515
 
279
516
  end
data/lib/jcr/jcr.rb CHANGED
@@ -15,6 +15,7 @@
15
15
  require 'optparse'
16
16
  require 'rubygems'
17
17
  require 'json'
18
+ require 'pp'
18
19
 
19
20
  require 'jcr/parser'
20
21
  require 'jcr/evaluate_rules'
@@ -26,7 +27,7 @@ require 'jcr/process_directives'
26
27
  module JCR
27
28
 
28
29
  class Context
29
- attr_accessor :mapping, :callbacks, :id, :tree, :roots, :catalog
30
+ attr_accessor :mapping, :callbacks, :id, :tree, :roots, :catalog, :trace
30
31
 
31
32
  def add_ruleset_alias( ruleset_alias, alias_uri )
32
33
  unless @catalog
@@ -56,7 +57,8 @@ module JCR
56
57
  JCR.evaluate_ruleset( data, self, root_name )
57
58
  end
58
59
 
59
- def initialize( ruleset = nil )
60
+ def initialize( ruleset = nil, trace = false )
61
+ @trace = trace
60
62
  if ruleset
61
63
  ingested = JCR.ingest_ruleset( ruleset, false, nil )
62
64
  @mapping = ingested.mapping
@@ -121,8 +123,9 @@ module JCR
121
123
 
122
124
  retval = nil
123
125
  root_rules.each do |r|
126
+ pp "Evaluating Root:", rule_to_s( r, false ) if ctx.trace
124
127
  raise "Root rules cannot be member rules" if r[:member_rule]
125
- retval = JCR.evaluate_rule( r, r, data, EvalConditions.new( ctx.mapping, ctx.callbacks ) )
128
+ retval = JCR.evaluate_rule( r, r, data, EvalConditions.new( ctx.mapping, ctx.callbacks, ctx.trace ) )
126
129
  break if retval.success
127
130
  end
128
131
 
@@ -163,6 +166,10 @@ module JCR
163
166
  options[:ruleset] = ruleset
164
167
  end
165
168
 
169
+ opt.on("--test-jcr", "parse and test the JCR only") do |testjcr|
170
+ options[:testjcr] = true
171
+ end
172
+
166
173
  opt.on("-S STRING","name of root rule. All roots will be tried if none is specified") do |root_name|
167
174
  if options[:root_name]
168
175
  puts "A root has already been specified. Use -h for help.", ""
@@ -214,35 +221,55 @@ module JCR
214
221
  return 2
215
222
  else
216
223
 
217
- ctx = Context.new( options[:ruleset] )
218
- if options[:overrides]
219
- options[:overrides].each do |ov|
220
- ctx.override!( ov )
224
+ begin
225
+
226
+ ctx = Context.new( options[:ruleset], options[:verbose] )
227
+ if options[:overrides]
228
+ options[:overrides].each do |ov|
229
+ ctx.override!( ov )
230
+ end
221
231
  end
222
- end
223
232
 
224
- if options[:json]
225
- data = JSON.parse( options[:json] )
226
- ec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
227
- return ec
228
- elsif $stdin.tty?
229
- ec = 0
230
- if my_argv.empty?
231
- ec = 2
232
- else
233
- my_argv.each do |fn|
234
- data = JSON.parse( File.open( fn ).read )
235
- tec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
236
- ec = tec if tec != 0 #record error but don't let non-error overwrite error
233
+ if options[:verbose]
234
+ pp "Ruleset Parse Tree", ctx.tree
235
+ pp "Ruleset Map"
236
+ ctx.mapping.each do |name,rule|
237
+ puts "Parsed Rule: #{name}"
238
+ puts rule_to_s( rule, false )
239
+ puts "Parsed Rule Structure: #{name}"
240
+ pp rule
237
241
  end
238
242
  end
239
- return ec
240
- else
241
- data = JSON.parse( ARGF.read )
242
- ec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
243
- return ec
244
- end
245
243
 
244
+ if options[:testjcr]
245
+ #we got this far which means the JCR was already parsed without
246
+ #issue. therefore return 0
247
+ return 0
248
+ elsif options[:json]
249
+ data = JSON.parse( options[:json] )
250
+ ec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
251
+ return ec
252
+ elsif $stdin.tty?
253
+ ec = 0
254
+ if my_argv.empty?
255
+ ec = 2
256
+ else
257
+ my_argv.each do |fn|
258
+ data = JSON.parse( File.open( fn ).read )
259
+ tec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
260
+ ec = tec if tec != 0 #record error but don't let non-error overwrite error
261
+ end
262
+ end
263
+ return ec
264
+ else
265
+ data = JSON.parse( ARGF.read )
266
+ ec = cli_eval( ctx, data, options[:root_name], options[:verbose] )
267
+ return ec
268
+ end
269
+
270
+ rescue Parslet::ParseFailed => failure
271
+ puts failure.cause.ascii_tree
272
+ end
246
273
  end
247
274
 
248
275
  end