jcrvalidator 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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