verifyanything 0.1.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.
data/lib/openverify.rb ADDED
@@ -0,0 +1,160 @@
1
+ require 'json'
2
+ require 'json_schemer'
3
+ require 'bigdecimal'
4
+
5
+ # Offline wire SDK. See README for the explicitly supported semantic subset.
6
+ module OpenVerify
7
+ SDK_VERSION = '0.1.0'
8
+ VCP_VERSION = 'draft'
9
+ PROVIDER_CONTRACT_VERSION = '0.1'
10
+ SPEC_REVISION = '5f17f402a270882f244a54dde25eda92a085eb39'
11
+ BUNDLE = JSON.parse(File.read(File.join(__dir__, 'openverify/bundle.json'))).freeze
12
+ class ProtocolError < StandardError
13
+ attr_reader :code, :path
14
+ def initialize(code, message = code, path = '')
15
+ @code, @path = code, path
16
+ super(message)
17
+ end
18
+ end
19
+ # Public JSON object model; absent keys and explicit null remain distinct.
20
+ class WireObject < Hash
21
+ def to_json(*args) = super(*args)
22
+ end
23
+ class StrictHash < Hash
24
+ def []=(key, value)
25
+ raise JSON::ParserError, 'duplicate JSON key' if key?(key)
26
+ super
27
+ end
28
+ end
29
+ def self.check(schema, value, code)
30
+ resolver = ->(uri) { BUNDLE['schemas'].fetch(uri.to_s) { raise ProtocolError.new('INVALID_PROVIDER_OUTPUT', 'Offline unknown reference') } }
31
+ validator = JSONSchemer.schema(numbers(schema), meta_schema: JSONSchemer.draft202012, format: false, ref_resolver: resolver)
32
+ raise ProtocolError.new(code) unless validator.valid?(numbers(value))
33
+ rescue JSONSchemer::InvalidRefResolution, JSONSchemer::UnknownRef => e
34
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT', e.message)
35
+ end
36
+ # JSON number equality must not inherit Ruby's Integer/Float hash distinction.
37
+ def self.numbers(value)
38
+ case value
39
+ when Numeric then BigDecimal(value.to_s)
40
+ when Hash then value.transform_values { |v| numbers(v) }
41
+ when Array then value.map { |v| numbers(v) }
42
+ else value
43
+ end
44
+ end
45
+ def self.validate_structure(contract, kind, value)
46
+ prefix = {'vcp'=>'urn:verification:draft:', 'provider'=>'urn:openverify:provider:0.1:'}[contract]
47
+ raise ProtocolError.new('SDK_UNSUPPORTED') unless prefix && (schema = BUNDLE['schemas'][prefix + kind])
48
+ if contract == 'vcp' && value.is_a?(Hash) && value.key?('protocol') && value['protocol'] != 'draft'
49
+ raise ProtocolError.new('PROTOCOL_VERSION')
50
+ end
51
+ if contract == 'provider' && kind == 'invocation' && value.is_a?(Hash) && value.key?('contract_version') && value['contract_version'] != '0.1'
52
+ raise ProtocolError.new('CONTRACT_VERSION_UNSUPPORTED')
53
+ end
54
+ check(schema, value, contract == 'vcp' ? 'PROTOCOL_STRUCTURE' : kind == 'invocation' ? 'INVALID_INVOCATION' : 'INVALID_PROVIDER_OUTPUT')
55
+ end
56
+ def self.validate_data(schema, value, code = 'INVALID_INPUT')
57
+ positions, refs = {}, []
58
+ visit = nil
59
+ visit = lambda do |s, path, top|
60
+ positions[path] = true
61
+ next if s == true || s == false
62
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') unless s.is_a?(Hash)
63
+ if (!top && s.key?('$id')) || %w[$anchor $dynamicAnchor $dynamicRef].any? { |k| s.key?(k) }
64
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT')
65
+ end
66
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') if s.key?('$schema') && s['$schema'] != 'https://json-schema.org/draft/2020-12/schema'
67
+ if s.key?('$ref')
68
+ ref = s['$ref']
69
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') unless ref.is_a?(String) && (ref == '#' || ref.start_with?('#/'))
70
+ refs << ref[1..]
71
+ end
72
+ %w[properties patternProperties $defs dependentSchemas].each do |k|
73
+ s.fetch(k, {}).each { |name, v| visit.call(v, "#{path}/#{k}/#{name.gsub('~','~0').gsub('/','~1')}", false) } if s.fetch(k, {}).is_a?(Hash)
74
+ end
75
+ %w[additionalProperties unevaluatedProperties propertyNames contains items unevaluatedItems not if then else contentSchema].each { |k| visit.call(s[k], "#{path}/#{k}", false) if s.key?(k) }
76
+ %w[allOf anyOf oneOf prefixItems].each { |k| s.fetch(k, []).each_with_index { |v, n| visit.call(v, "#{path}/#{k}/#{n}", false) } if s.fetch(k, []).is_a?(Array) }
77
+ end
78
+ visit.call(schema, '', true)
79
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') unless refs.all? { |r| positions[r] }
80
+ validator = JSONSchemer.schema(numbers(schema), meta_schema: JSONSchemer.draft202012, format: false, ref_resolver: ->(_) { raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') })
81
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') unless validator.valid_schema?
82
+ raise ProtocolError.new(code) unless validator.valid?(numbers(value))
83
+ end
84
+ def self.validate(contract, kind, value)
85
+ validate_structure(contract, kind, value)
86
+ raise ProtocolError.new('SDK_UNSUPPORTED') if contract == 'vcp' && !BUNDLE['semantic_vcp_types'].include?(kind)
87
+ BUNDLE['rules'].fetch(kind, []).each { |r| check(r['schema'], value, contract == 'provider' ? 'INVALID_PROVIDER_OUTPUT' : r['code']) }
88
+ if kind == 'resource' && value['kind'] == 'RANGE' && value['minimum'] > value['maximum']
89
+ raise ProtocolError.new('RESOURCE_MEASUREMENT')
90
+ end
91
+ if contract == 'vcp'
92
+ {'extensions'=>'extension', 'evidence'=>'evidence'}.each { |k, target| value.fetch(k, []).each { |v| validate('vcp', target, v) } }
93
+ validate('vcp', 'artifact-ref', value['artifact_ref']) if value['artifact_ref']
94
+ value.fetch('usage', {}).each_value { |v| validate('vcp', 'resource', v) }
95
+ else
96
+ raise ProtocolError.new('SDK_UNSUPPORTED') if value.key?('deadline') || value.key?('timestamp')
97
+ if kind == 'provider-event'
98
+ begin
99
+ target = {'evidence'=>'evidence','artifact'=>'artifact-ref'}[value['kind']]
100
+ validate('vcp',target,value['payload']) if target
101
+ value['payload'].each_value { |r| validate('vcp','resource',r) } if value['kind'] == 'resource_usage'
102
+ rescue ProtocolError
103
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT')
104
+ end
105
+ end
106
+ if kind == 'provider-manifest'
107
+ seen = {}
108
+ value['capabilities'].each do |c|
109
+ key = [c['capability_id'], c['capability_version']]
110
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') if seen[key]
111
+ seen[key] = true
112
+ raise ProtocolError.new('INVALID_PROVIDER_OUTPUT') if c.key?('vcp_version') && !value['supported_vcp_versions'].include?(c['vcp_version'])
113
+ validate('provider', 'capability', c)
114
+ end
115
+ end
116
+ if kind == 'capability'
117
+ %w[input_schema config_schema output_schema].each do |k|
118
+ begin
119
+ validate_data(value[k], nil)
120
+ rescue ProtocolError => e
121
+ raise unless e.code == 'INVALID_INPUT'
122
+ end
123
+ end
124
+ end
125
+ validate('provider', 'provider-manifest', value['manifest']) if kind == 'initialize-result'
126
+ raise ProtocolError.new(kind == 'invocation' ? 'INVALID_INVOCATION' : 'INVALID_PROVIDER_OUTPUT') if value.key?('vcp_version') && value['vcp_version'] != 'draft'
127
+ %w[resource_budget resource_usage].each do |k|
128
+ value.fetch(k, {}).each_value do |r|
129
+ begin
130
+ validate('vcp', 'resource', r)
131
+ rescue ProtocolError
132
+ raise ProtocolError.new(kind == 'invocation' ? 'INVALID_INVOCATION' : 'INVALID_PROVIDER_OUTPUT')
133
+ end
134
+ raise ProtocolError.new('INVALID_INVOCATION') if k == 'resource_budget' && (r['kind'] != 'EXACT' || value['vcp_version'] != 'draft')
135
+ end
136
+ end
137
+ end
138
+ WireObject[value]
139
+ end
140
+ def self.parse(contract, kind, text)
141
+ begin
142
+ value = JSON.parse(text, object_class: StrictHash, allow_nan: false, allow_duplicate_key: false)
143
+ precise = JSON.parse(text, decimal_class: BigDecimal, allow_nan: false, allow_duplicate_key: false)
144
+ faithful = lambda do |native, exact|
145
+ if native.is_a?(Float)
146
+ raise JSON::ParserError, 'JSON number cannot be represented faithfully' unless native.finite? && BigDecimal(native.to_s) == exact
147
+ elsif native.is_a?(Hash)
148
+ native.each { |k, v| faithful.call(v, exact[k]) }
149
+ elsif native.is_a?(Array)
150
+ native.each_with_index { |v, n| faithful.call(v, exact[n]) }
151
+ end
152
+ end
153
+ faithful.call(value, precise)
154
+ rescue JSON::ParserError, EncodingError => e
155
+ raise ProtocolError.new(contract == 'vcp' ? 'PROTOCOL_JSON' : kind == 'invocation' ? 'INVALID_INVOCATION' : 'INVALID_PROVIDER_OUTPUT', e.message)
156
+ end
157
+ validate(contract, kind, value)
158
+ end
159
+ def self.serialize(value) = JSON.generate(value, allow_nan: false)
160
+ end
@@ -0,0 +1,3 @@
1
+ # Public VerifyAnything entrypoint; retain the original OpenVerify module API.
2
+ require_relative 'openverify'
3
+ VerifyAnything = OpenVerify
metadata ADDED
@@ -0,0 +1,58 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: verifyanything
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - OpenVerify contributors
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-10-17 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: json_schemer
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.5'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.5'
26
+ executables: []
27
+ extensions: []
28
+ extra_rdoc_files: []
29
+ files:
30
+ - LICENSE
31
+ - README.md
32
+ - lib/openverify.rb
33
+ - lib/openverify/bundle.json
34
+ - lib/verifyanything.rb
35
+ homepage: https://github.com/openverifyproject/openverify
36
+ licenses:
37
+ - MIT
38
+ metadata:
39
+ source_code_uri: https://github.com/openverifyproject/openverify
40
+ rubygems_mfa_required: 'true'
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '3.2'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubygems_version: 3.6.9
56
+ specification_version: 4
57
+ summary: VerifyAnything DRAFT VCP and Provider Contract wire SDK
58
+ test_files: []