oso-oso 0.2.2 → 0.4.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.
- checksums.yaml +5 -5
- data/.rubocop.yml +7 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +13 -12
- data/Makefile +12 -9
- data/README.md +6 -9
- data/Rakefile +0 -1
- data/bin/oso +7 -0
- data/ext/oso-oso/lib/libpolar.dylib +0 -0
- data/ext/oso-oso/lib/libpolar.so +0 -0
- data/ext/oso-oso/lib/polar.dll +0 -0
- data/lib/oso/http.rb +1 -1
- data/lib/oso/oso.rb +6 -32
- data/lib/oso/path_mapper.rb +1 -1
- data/lib/oso/polar.rb +1 -0
- data/lib/oso/polar/errors.rb +25 -9
- data/lib/oso/polar/ffi.rb +6 -2
- data/lib/oso/polar/ffi/error.rb +2 -2
- data/lib/oso/polar/ffi/polar.rb +6 -16
- data/lib/oso/polar/ffi/query.rb +9 -0
- data/lib/oso/polar/host.rb +246 -0
- data/lib/oso/polar/polar.rb +117 -304
- data/lib/oso/polar/query.rb +92 -36
- data/lib/oso/version.rb +1 -1
- data/oso-oso.gemspec +11 -6
- metadata +30 -10
- data/bin/console +0 -33
- data/bin/setup +0 -6
data/lib/oso/polar/query.rb
CHANGED
@@ -3,23 +3,57 @@
|
|
3
3
|
module Oso
|
4
4
|
module Polar
|
5
5
|
# A single Polar query.
|
6
|
-
class Query
|
6
|
+
class Query # rubocop:disable Metrics/ClassLength
|
7
|
+
# @return [Enumerator]
|
7
8
|
attr_reader :results
|
8
9
|
|
9
|
-
# @param
|
10
|
-
# @param
|
11
|
-
def initialize(
|
12
|
-
@
|
13
|
-
@
|
10
|
+
# @param ffi_query [FFI::Query]
|
11
|
+
# @param host [Oso::Polar::Host]
|
12
|
+
def initialize(ffi_query, host:)
|
13
|
+
@calls = {}
|
14
|
+
@ffi_query = ffi_query
|
15
|
+
@host = host
|
14
16
|
@results = start
|
15
17
|
end
|
16
18
|
|
17
19
|
private
|
18
20
|
|
21
|
+
# @return [Hash<Integer, Enumerator>]
|
22
|
+
attr_reader :calls
|
19
23
|
# @return [FFI::Query]
|
20
|
-
attr_reader :
|
21
|
-
# @return [
|
22
|
-
attr_reader :
|
24
|
+
attr_reader :ffi_query
|
25
|
+
# @return [Host]
|
26
|
+
attr_reader :host
|
27
|
+
|
28
|
+
# Send result of predicate check across FFI boundary.
|
29
|
+
#
|
30
|
+
# @param result [Boolean]
|
31
|
+
# @param call_id [Integer]
|
32
|
+
# @raise [Error] if the FFI call raises one.
|
33
|
+
def question_result(result, call_id:)
|
34
|
+
ffi_query.question_result(result, call_id: call_id)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Register a Ruby method call, wrapping the call result in a generator if
|
38
|
+
# it isn't already one.
|
39
|
+
#
|
40
|
+
# @param method [#to_sym]
|
41
|
+
# @param call_id [Integer]
|
42
|
+
# @param instance [Hash<String, Object>]
|
43
|
+
# @param args [Array<Hash>]
|
44
|
+
# @raise [InvalidCallError] if the method doesn't exist on the instance or
|
45
|
+
# the args passed to the method are invalid.
|
46
|
+
def register_call(method, call_id:, instance:, args:)
|
47
|
+
return if calls.key?(call_id)
|
48
|
+
|
49
|
+
args = args.map { |a| host.to_ruby(a) }
|
50
|
+
instance = host.to_ruby(instance)
|
51
|
+
result = instance.__send__(method, *args)
|
52
|
+
result = [result].to_enum unless result.is_a? Enumerator # Call must be a generator.
|
53
|
+
calls[call_id] = result.lazy
|
54
|
+
rescue ArgumentError, NoMethodError
|
55
|
+
raise InvalidCallError
|
56
|
+
end
|
23
57
|
|
24
58
|
# Send next result of Ruby method call across FFI boundary.
|
25
59
|
#
|
@@ -27,16 +61,24 @@ module Oso
|
|
27
61
|
# @param call_id [Integer]
|
28
62
|
# @raise [Error] if the FFI call raises one.
|
29
63
|
def call_result(result, call_id:)
|
30
|
-
|
64
|
+
ffi_query.call_result(result, call_id: call_id)
|
31
65
|
end
|
32
66
|
|
33
|
-
#
|
67
|
+
# Retrieve the next result from a registered call and pass it to {#to_polar_term}.
|
34
68
|
#
|
35
|
-
# @param
|
36
|
-
# @
|
69
|
+
# @param id [Integer]
|
70
|
+
# @return [Hash]
|
71
|
+
# @raise [StopIteration] if the call has been exhausted.
|
72
|
+
def next_call_result(id)
|
73
|
+
host.to_polar_term(calls[id].next)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Send application error across FFI boundary.
|
77
|
+
#
|
78
|
+
# @param message [String]
|
37
79
|
# @raise [Error] if the FFI call raises one.
|
38
|
-
def
|
39
|
-
|
80
|
+
def application_error(message)
|
81
|
+
ffi_query.application_error(message)
|
40
82
|
end
|
41
83
|
|
42
84
|
# Fetch the next result from calling a Ruby method and prepare it for
|
@@ -45,16 +87,17 @@ module Oso
|
|
45
87
|
# @param method [#to_sym]
|
46
88
|
# @param args [Array<Hash>]
|
47
89
|
# @param call_id [Integer]
|
48
|
-
# @param
|
90
|
+
# @param instance [Hash<String, Object>]
|
49
91
|
# @raise [Error] if the FFI call raises one.
|
50
92
|
def handle_call(method, call_id:, instance:, args:)
|
51
|
-
|
52
|
-
result = JSON.dump(
|
93
|
+
register_call(method, call_id: call_id, instance: instance, args: args)
|
94
|
+
result = JSON.dump(next_call_result(call_id))
|
53
95
|
call_result(result, call_id: call_id)
|
54
|
-
rescue InvalidCallError
|
96
|
+
rescue InvalidCallError => e
|
97
|
+
application_error(e.message)
|
98
|
+
call_result(nil, call_id: call_id)
|
99
|
+
rescue StopIteration
|
55
100
|
call_result(nil, call_id: call_id)
|
56
|
-
# @TODO: polar line numbers in errors once polar errors are better.
|
57
|
-
# raise PolarRuntimeError(f"Error calling {attribute}")
|
58
101
|
end
|
59
102
|
|
60
103
|
# Create a generator that can be polled to advance the query loop.
|
@@ -62,22 +105,31 @@ module Oso
|
|
62
105
|
# @yieldparam [Hash<String, Object>]
|
63
106
|
# @return [Enumerator]
|
64
107
|
# @raise [Error] if any of the FFI calls raise one.
|
65
|
-
def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
108
|
+
def start # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
66
109
|
Enumerator.new do |yielder| # rubocop:disable Metrics/BlockLength
|
67
110
|
loop do # rubocop:disable Metrics/BlockLength
|
68
|
-
event =
|
111
|
+
event = ffi_query.next_event
|
69
112
|
case event.kind
|
70
113
|
when 'Done'
|
71
114
|
break
|
72
115
|
when 'Result'
|
73
|
-
yielder << event.data['bindings'].transform_values { |v|
|
116
|
+
yielder << event.data['bindings'].transform_values { |v| host.to_ruby(v) }
|
74
117
|
when 'MakeExternal'
|
75
118
|
id = event.data['instance_id']
|
76
|
-
raise DuplicateInstanceRegistrationError, id if
|
119
|
+
raise DuplicateInstanceRegistrationError, id if host.instance? id
|
77
120
|
|
78
|
-
|
79
|
-
|
80
|
-
|
121
|
+
constructor = event.data['constructor']['value']
|
122
|
+
if constructor.key? 'InstanceLiteral'
|
123
|
+
cls_name = constructor['InstanceLiteral']['tag']
|
124
|
+
fields = constructor['InstanceLiteral']['fields']['fields']
|
125
|
+
initargs = Hash[fields.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
|
126
|
+
elsif constructor.key? 'Call'
|
127
|
+
cls_name = constructor['Call']['name']
|
128
|
+
initargs = constructor['Call']['args'].map { |arg| host.to_ruby(arg) }
|
129
|
+
else
|
130
|
+
raise InvalidConstructorError
|
131
|
+
end
|
132
|
+
host.make_instance(cls_name, initargs: initargs, id: id)
|
81
133
|
when 'ExternalCall'
|
82
134
|
call_id = event.data['call_id']
|
83
135
|
instance = event.data['instance']
|
@@ -88,24 +140,28 @@ module Oso
|
|
88
140
|
instance_id = event.data['instance_id']
|
89
141
|
left_tag = event.data['left_class_tag']
|
90
142
|
right_tag = event.data['right_class_tag']
|
91
|
-
answer =
|
143
|
+
answer = host.subspecializer?(instance_id, left_tag: left_tag, right_tag: right_tag)
|
92
144
|
question_result(answer, call_id: event.data['call_id'])
|
93
145
|
when 'ExternalIsa'
|
94
|
-
|
146
|
+
instance = event.data['instance']
|
95
147
|
class_tag = event.data['class_tag']
|
96
|
-
answer =
|
148
|
+
answer = host.isa?(instance, class_tag: class_tag)
|
97
149
|
question_result(answer, call_id: event.data['call_id'])
|
98
150
|
when 'ExternalUnify'
|
99
151
|
left_instance_id = event.data['left_instance_id']
|
100
152
|
right_instance_id = event.data['right_instance_id']
|
101
|
-
answer =
|
153
|
+
answer = host.unify?(left_instance_id, right_instance_id)
|
102
154
|
question_result(answer, call_id: event.data['call_id'])
|
103
155
|
when 'Debug'
|
104
156
|
puts event.data['message'] if event.data['message']
|
105
|
-
print '> '
|
106
|
-
|
107
|
-
|
108
|
-
|
157
|
+
print 'debug> '
|
158
|
+
begin
|
159
|
+
input = $stdin.readline.chomp.chomp(';')
|
160
|
+
rescue EOFError
|
161
|
+
next
|
162
|
+
end
|
163
|
+
command = JSON.dump(host.to_polar_term(input))
|
164
|
+
ffi_query.debug_command(command)
|
109
165
|
else
|
110
166
|
raise "Unhandled event: #{JSON.dump(event.inspect)}"
|
111
167
|
end
|
data/lib/oso/version.rb
CHANGED
data/oso-oso.gemspec
CHANGED
@@ -5,22 +5,26 @@ require_relative 'lib/oso/version'
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = 'oso-oso'
|
7
7
|
spec.version = Oso::VERSION
|
8
|
-
spec.authors = ['Oso Security']
|
8
|
+
spec.authors = ['Oso Security, Inc.']
|
9
9
|
spec.email = ['support@osohq.com']
|
10
|
-
|
11
|
-
spec.summary = '
|
10
|
+
spec.licenses = ['Apache-2.0']
|
11
|
+
spec.summary = 'oso authorization library.'
|
12
12
|
spec.homepage = 'https://www.osohq.com/'
|
13
|
+
|
13
14
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.4.0')
|
14
15
|
|
15
16
|
spec.metadata['homepage_uri'] = spec.homepage
|
17
|
+
spec.metadata['source_code_uri'] = 'https://github.com/osohq/oso'
|
16
18
|
|
17
19
|
# Specify which files should be added to the gem when it is released.
|
18
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
21
|
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
20
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
files + Dir['ext/oso-oso/lib/*']
|
21
24
|
end
|
22
|
-
|
23
|
-
spec.
|
25
|
+
|
26
|
+
spec.bindir = 'bin'
|
27
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
28
|
spec.require_paths = ['lib']
|
25
29
|
|
26
30
|
# Runtime dependencies
|
@@ -30,6 +34,7 @@ Gem::Specification.new do |spec|
|
|
30
34
|
spec.add_development_dependency 'pry-byebug', '~> 3.9.0'
|
31
35
|
spec.add_development_dependency 'rake', '~> 12.0'
|
32
36
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
|
+
spec.add_development_dependency 'rubocop', '~> 0.89.0'
|
33
38
|
spec.add_development_dependency 'solargraph', '~> 0.39.8'
|
34
39
|
spec.add_development_dependency 'yard', '~> 0.9.25'
|
35
40
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oso-oso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Oso Security
|
7
|
+
- Oso Security, Inc.
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.89.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.89.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: solargraph
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -97,22 +111,24 @@ dependencies:
|
|
97
111
|
description:
|
98
112
|
email:
|
99
113
|
- support@osohq.com
|
100
|
-
executables:
|
114
|
+
executables:
|
115
|
+
- oso
|
101
116
|
extensions: []
|
102
117
|
extra_rdoc_files: []
|
103
118
|
files:
|
104
119
|
- ".gitignore"
|
105
120
|
- ".rspec"
|
121
|
+
- ".rubocop.yml"
|
106
122
|
- ".solargraph.yml"
|
107
123
|
- Gemfile
|
108
124
|
- Gemfile.lock
|
109
125
|
- Makefile
|
110
126
|
- README.md
|
111
127
|
- Rakefile
|
112
|
-
- bin/
|
113
|
-
- bin/setup
|
128
|
+
- bin/oso
|
114
129
|
- ext/oso-oso/lib/libpolar.dylib
|
115
130
|
- ext/oso-oso/lib/libpolar.so
|
131
|
+
- ext/oso-oso/lib/polar.dll
|
116
132
|
- lib/oso.rb
|
117
133
|
- lib/oso/http.rb
|
118
134
|
- lib/oso/oso.rb
|
@@ -124,6 +140,7 @@ files:
|
|
124
140
|
- lib/oso/polar/ffi/polar.rb
|
125
141
|
- lib/oso/polar/ffi/query.rb
|
126
142
|
- lib/oso/polar/ffi/query_event.rb
|
143
|
+
- lib/oso/polar/host.rb
|
127
144
|
- lib/oso/polar/polar.rb
|
128
145
|
- lib/oso/polar/predicate.rb
|
129
146
|
- lib/oso/polar/query.rb
|
@@ -132,9 +149,11 @@ files:
|
|
132
149
|
- lib/oso/version.rb
|
133
150
|
- oso-oso.gemspec
|
134
151
|
homepage: https://www.osohq.com/
|
135
|
-
licenses:
|
152
|
+
licenses:
|
153
|
+
- Apache-2.0
|
136
154
|
metadata:
|
137
155
|
homepage_uri: https://www.osohq.com/
|
156
|
+
source_code_uri: https://github.com/osohq/oso
|
138
157
|
post_install_message:
|
139
158
|
rdoc_options: []
|
140
159
|
require_paths:
|
@@ -150,8 +169,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
150
169
|
- !ruby/object:Gem::Version
|
151
170
|
version: '0'
|
152
171
|
requirements: []
|
153
|
-
|
172
|
+
rubyforge_project:
|
173
|
+
rubygems_version: 2.6.14.4
|
154
174
|
signing_key:
|
155
175
|
specification_version: 4
|
156
|
-
summary:
|
176
|
+
summary: oso authorization library.
|
157
177
|
test_files: []
|
data/bin/console
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
# frozen_string_literal: true
|
3
|
-
|
4
|
-
require 'bundler/setup'
|
5
|
-
require 'oso'
|
6
|
-
|
7
|
-
polar = Oso::Polar.new.tap do |p| # rubocop:disable Lint/UselessAssignment
|
8
|
-
p.load_str('f(1); f(2); g(1); g(2); h(2); k(x) := f(x), h(x), g(x);')
|
9
|
-
puts 'f(x)', p.send(:query_str, 'f(x)').to_a
|
10
|
-
puts 'k(x)', p.send(:query_str, 'k(x)').to_a
|
11
|
-
|
12
|
-
p.load_str('foo(1, 2); foo(3, 4); foo(5, 6);')
|
13
|
-
expected = [{ 'x' => 1, 'y' => 2 }, { 'x' => 3, 'y' => 4 }, { 'x' => 5, 'y' => 6 }]
|
14
|
-
raise 'AssertionError' if p.send(:query_str, 'foo(x, y)').to_a != expected
|
15
|
-
|
16
|
-
class TestClass # rubocop:disable Style/Documentation
|
17
|
-
def my_method
|
18
|
-
1
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
p.register_class(TestClass)
|
23
|
-
|
24
|
-
p.load_str('external(x, 3) := x = new TestClass{}.my_method;')
|
25
|
-
results = p.send(:query_str, 'external(1, x)')
|
26
|
-
p results.next
|
27
|
-
|
28
|
-
# p.load_str('testDebug() := debug(), foo(x, y), k(y);')
|
29
|
-
# p.send(:query_str, 'testDebug()').next
|
30
|
-
end
|
31
|
-
|
32
|
-
require 'pry'
|
33
|
-
Pry.start
|