oso-oso 0.6.0 → 0.8.2
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 +4 -4
- data/Gemfile.lock +1 -1
- 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/polar/errors.rb +7 -1
- data/lib/oso/polar/ffi/error.rb +2 -0
- data/lib/oso/polar/ffi/message.rb +2 -0
- data/lib/oso/polar/ffi/polar.rb +11 -1
- data/lib/oso/polar/ffi/query.rb +2 -0
- data/lib/oso/polar/host.rb +18 -49
- data/lib/oso/polar/polar.rb +30 -22
- data/lib/oso/polar/query.rb +32 -31
- data/lib/oso/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d51e82a017eaa54ea9a33b9199461015e3eac36
|
4
|
+
data.tar.gz: 6903b31bf79562138d28d96c1c33d51270f65ba5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91cc12a5af5fc9b16492681e398b8d09d341ac4dd4af34ca0b216c774248024723844937b1c106e7aceba03dc574b6377530cb74f2e591c86cab62ae836538df
|
7
|
+
data.tar.gz: 1cd76a3ba326f3e505e956f22f41ae1a572bc995e990be19e294dae803bd0683f01b7d3589ca4a44923848deb150ccb401ee77bfaf99faacc6e9b032a61b0fe7
|
data/Gemfile.lock
CHANGED
Binary file
|
data/ext/oso-oso/lib/libpolar.so
CHANGED
Binary file
|
data/ext/oso-oso/lib/polar.dll
CHANGED
Binary file
|
data/lib/oso/polar/errors.rb
CHANGED
@@ -32,13 +32,13 @@ module Oso
|
|
32
32
|
# Errors originating from this side of the FFI boundary.
|
33
33
|
|
34
34
|
class UnregisteredClassError < PolarRuntimeError; end
|
35
|
-
class MissingConstructorError < PolarRuntimeError; end
|
36
35
|
class UnregisteredInstanceError < PolarRuntimeError; end
|
37
36
|
class DuplicateInstanceRegistrationError < PolarRuntimeError; end
|
38
37
|
|
39
38
|
# TODO: I think this should probably have some arguments to say what the call is
|
40
39
|
class InvalidCallError < PolarRuntimeError; end
|
41
40
|
class InvalidConstructorError < PolarRuntimeError; end
|
41
|
+
class InvalidIteratorError < PolarRuntimeError; end
|
42
42
|
class InvalidQueryTypeError < PolarRuntimeError; end
|
43
43
|
class NullByteInPolarFileError < PolarRuntimeError; end
|
44
44
|
class UnexpectedPolarTypeError < PolarRuntimeError; end
|
@@ -68,6 +68,12 @@ module Oso
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
+
class UnimplementedOperationError < PolarRuntimeError # rubocop:disable Style/Documentation
|
72
|
+
def initialize(operation)
|
73
|
+
super("#{operation} are unimplemented in the oso Ruby library")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
71
77
|
# Generic operational exception.
|
72
78
|
class OperationalError < Error; end
|
73
79
|
class UnknownError < OperationalError; end
|
data/lib/oso/polar/ffi/error.rb
CHANGED
data/lib/oso/polar/ffi/polar.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
module FFI
|
@@ -11,6 +13,7 @@ module Oso
|
|
11
13
|
|
12
14
|
attach_function :new, :polar_new, [], FFI::Polar
|
13
15
|
attach_function :load, :polar_load, [FFI::Polar, :string, :string], :int32
|
16
|
+
attach_function :clear_rules, :polar_clear_rules, [FFI::Polar], :int32
|
14
17
|
attach_function :next_inline_query, :polar_next_inline_query, [FFI::Polar, :uint32], FFI::Query
|
15
18
|
attach_function :new_id, :polar_get_external_id, [FFI::Polar], :uint64
|
16
19
|
attach_function :new_query_from_str, :polar_new_query, [FFI::Polar, :string, :uint32], FFI::Query
|
@@ -39,6 +42,13 @@ module Oso
|
|
39
42
|
raise FFI::Error.get if loaded.zero?
|
40
43
|
end
|
41
44
|
|
45
|
+
# @raise [FFI::Error] if the FFI call returns an error.
|
46
|
+
def clear_rules
|
47
|
+
cleared = Rust.clear_rules(self)
|
48
|
+
process_messages
|
49
|
+
raise FFI::Error.get if cleared.zero?
|
50
|
+
end
|
51
|
+
|
42
52
|
# @return [FFI::Query] if there are remaining inline queries.
|
43
53
|
# @return [nil] if there are no remaining inline queries.
|
44
54
|
# @raise [FFI::Error] if the FFI call returns an error.
|
@@ -84,7 +94,7 @@ module Oso
|
|
84
94
|
# @param name [String]
|
85
95
|
# @param value [Hash<String, Object>]
|
86
96
|
# @raise [FFI::Error] if the FFI call returns an error.
|
87
|
-
def register_constant(
|
97
|
+
def register_constant(value, name:)
|
88
98
|
registered = Rust.register_constant(self, name, JSON.dump(value))
|
89
99
|
raise FFI::Error.get if registered.zero?
|
90
100
|
end
|
data/lib/oso/polar/ffi/query.rb
CHANGED
data/lib/oso/polar/host.rb
CHANGED
@@ -10,8 +10,6 @@ module Oso
|
|
10
10
|
attr_reader :ffi_polar
|
11
11
|
# @return [Hash<String, Class>]
|
12
12
|
attr_reader :classes
|
13
|
-
# @return [Hash<String, Object>]
|
14
|
-
attr_reader :constructors
|
15
13
|
# @return [Hash<Integer, Object>]
|
16
14
|
attr_reader :instances
|
17
15
|
|
@@ -20,14 +18,12 @@ module Oso
|
|
20
18
|
def initialize(ffi_polar)
|
21
19
|
@ffi_polar = ffi_polar
|
22
20
|
@classes = {}
|
23
|
-
@constructors = {}
|
24
21
|
@instances = {}
|
25
22
|
end
|
26
23
|
|
27
24
|
def initialize_copy(other)
|
28
25
|
@ffi_polar = other.ffi_polar
|
29
26
|
@classes = other.classes.dup
|
30
|
-
@constructors = other.constructors.dup
|
31
27
|
@instances = other.instances.dup
|
32
28
|
end
|
33
29
|
|
@@ -44,38 +40,18 @@ module Oso
|
|
44
40
|
|
45
41
|
# Store a Ruby class in the {#classes} cache.
|
46
42
|
#
|
47
|
-
# @param cls [Class] the class to cache
|
48
|
-
# @param name [String] the name to cache the class as.
|
49
|
-
# @param constructor [Proc] optional custom constructor function. Defaults to the :new method.
|
43
|
+
# @param cls [Class] the class to cache.
|
44
|
+
# @param name [String] the name to cache the class as.
|
50
45
|
# @return [String] the name the class is cached as.
|
51
|
-
# @raise [
|
52
|
-
|
53
|
-
|
46
|
+
# @raise [DuplicateClassAliasError] if attempting to register a class
|
47
|
+
# under a previously-registered name.
|
48
|
+
def cache_class(cls, name:)
|
54
49
|
raise DuplicateClassAliasError, name: name, old: get_class(name), new: cls if classes.key? name
|
55
50
|
|
56
51
|
classes[name] = cls
|
57
|
-
if constructor.nil?
|
58
|
-
constructors[name] = :new
|
59
|
-
elsif constructor.respond_to? :call
|
60
|
-
constructors[name] = constructor
|
61
|
-
else
|
62
|
-
raise InvalidConstructorError
|
63
|
-
end
|
64
52
|
name
|
65
53
|
end
|
66
54
|
|
67
|
-
# Fetch a constructor from the {#constructors} cache.
|
68
|
-
#
|
69
|
-
# @param name [String]
|
70
|
-
# @return [Symbol] if constructor is the default of `:new`.
|
71
|
-
# @return [Proc] if a custom constructor was registered.
|
72
|
-
# @raise [MissingConstructorError] if the constructor has not been registered.
|
73
|
-
def get_constructor(name)
|
74
|
-
raise MissingConstructorError, name unless constructors.key? name
|
75
|
-
|
76
|
-
constructors[name]
|
77
|
-
end
|
78
|
-
|
79
55
|
# Check if an instance exists in the {#instances} cache.
|
80
56
|
#
|
81
57
|
# @param id [Integer]
|
@@ -100,12 +76,12 @@ module Oso
|
|
100
76
|
instances[id]
|
101
77
|
end
|
102
78
|
|
103
|
-
# Cache a Ruby instance in the {#instances} cache, fetching a
|
104
|
-
#
|
79
|
+
# Cache a Ruby instance in the {#instances} cache, fetching a new id if
|
80
|
+
# one isn't provided.
|
105
81
|
#
|
106
82
|
# @param instance [Object]
|
107
|
-
# @param id [Integer]
|
108
|
-
# @return [Integer]
|
83
|
+
# @param id [Integer] the instance ID. Generated via FFI if not provided.
|
84
|
+
# @return [Integer] the instance ID.
|
109
85
|
def cache_instance(instance, id: nil)
|
110
86
|
id = ffi_polar.new_id if id.nil?
|
111
87
|
instances[id] = instance
|
@@ -114,24 +90,17 @@ module Oso
|
|
114
90
|
|
115
91
|
# Construct and cache a Ruby instance.
|
116
92
|
#
|
117
|
-
# @param cls_name [String]
|
118
|
-
# @param args [Array<Object>]
|
119
|
-
# @param kwargs [Hash<String, Object>]
|
120
|
-
# @param id [Integer]
|
93
|
+
# @param cls_name [String] name of the instance's class.
|
94
|
+
# @param args [Array<Object>] positional args to the constructor.
|
95
|
+
# @param kwargs [Hash<String, Object>] keyword args to the constructor.
|
96
|
+
# @param id [Integer] the instance ID.
|
121
97
|
# @raise [PolarRuntimeError] if instance construction fails.
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
if kwargs.empty?
|
127
|
-
get_class(cls_name).__send__(:new, *args)
|
128
|
-
else
|
129
|
-
get_class(cls_name).__send__(:new, *args, **kwargs)
|
130
|
-
end
|
131
|
-
elsif kwargs.empty?
|
132
|
-
constructor.call(*args)
|
98
|
+
# @return [Integer] the instance ID.
|
99
|
+
def make_instance(cls_name, args:, kwargs:, id:)
|
100
|
+
instance = if kwargs.empty? # This check is for Ruby < 2.7.
|
101
|
+
get_class(cls_name).__send__(:new, *args)
|
133
102
|
else
|
134
|
-
|
103
|
+
get_class(cls_name).__send__(:new, *args, **kwargs)
|
135
104
|
end
|
136
105
|
cache_instance(instance, id: id)
|
137
106
|
rescue StandardError => e
|
data/lib/oso/polar/polar.rb
CHANGED
@@ -1,10 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'json'
|
4
|
-
require 'pp'
|
5
|
-
require 'set'
|
6
|
-
require 'digest/md5'
|
7
|
-
|
8
3
|
# Missing Ruby type.
|
9
4
|
module PolarBoolean; end
|
10
5
|
# Monkey-patch Ruby true type.
|
@@ -43,6 +38,9 @@ module Oso
|
|
43
38
|
@ffi_polar = FFI::Polar.create
|
44
39
|
@host = Host.new(ffi_polar)
|
45
40
|
|
41
|
+
# Register global constants.
|
42
|
+
register_constant nil, name: 'nil'
|
43
|
+
|
46
44
|
# Register built-in classes.
|
47
45
|
register_class PolarBoolean, name: 'Boolean'
|
48
46
|
register_class Integer
|
@@ -52,9 +50,12 @@ module Oso
|
|
52
50
|
register_class String
|
53
51
|
end
|
54
52
|
|
55
|
-
#
|
56
|
-
|
57
|
-
|
53
|
+
# Clear all rules and rule sources from the current Polar instance
|
54
|
+
#
|
55
|
+
# @return [self] for chaining.
|
56
|
+
def clear_rules
|
57
|
+
ffi_polar.clear_rules
|
58
|
+
self
|
58
59
|
end
|
59
60
|
|
60
61
|
# Load a Polar policy file.
|
@@ -62,6 +63,7 @@ module Oso
|
|
62
63
|
# @param name [String]
|
63
64
|
# @raise [PolarFileExtensionError] if provided filename has invalid extension.
|
64
65
|
# @raise [PolarFileNotFoundError] if provided filename does not exist.
|
66
|
+
# @return [self] for chaining.
|
65
67
|
def load_file(name)
|
66
68
|
raise PolarFileExtensionError, name unless File.extname(name) == '.polar'
|
67
69
|
|
@@ -78,6 +80,7 @@ module Oso
|
|
78
80
|
# @raise [NullByteInPolarFileError] if str includes a non-terminating null byte.
|
79
81
|
# @raise [InlineQueryFailedError] on the first failed inline query.
|
80
82
|
# @raise [Error] if any of the FFI calls raise one.
|
83
|
+
# @return [self] for chaining.
|
81
84
|
def load_str(str, filename: nil) # rubocop:disable Metrics/MethodLength
|
82
85
|
raise NullByteInPolarFileError if str.chomp("\0").include?("\0")
|
83
86
|
|
@@ -92,6 +95,7 @@ module Oso
|
|
92
95
|
raise InlineQueryFailedError, next_query.source
|
93
96
|
end
|
94
97
|
end
|
98
|
+
self
|
95
99
|
end
|
96
100
|
|
97
101
|
# Query for a Polar predicate or string.
|
@@ -129,18 +133,26 @@ module Oso
|
|
129
133
|
|
130
134
|
# Register a Ruby class with Polar.
|
131
135
|
#
|
132
|
-
# @param cls [Class]
|
133
|
-
# @param name [String]
|
134
|
-
# @
|
135
|
-
#
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
136
|
+
# @param cls [Class] the class to register.
|
137
|
+
# @param name [String] the name to register the class as. Defaults to the name of the class.
|
138
|
+
# @raise [DuplicateClassAliasError] if attempting to register a class
|
139
|
+
# under a previously-registered name.
|
140
|
+
# @raise [FFI::Error] if the FFI call returns an error.
|
141
|
+
# @return [self] for chaining.
|
142
|
+
def register_class(cls, name: nil)
|
143
|
+
name = host.cache_class(cls, name: name || cls.name)
|
144
|
+
register_constant(cls, name: name)
|
140
145
|
end
|
141
146
|
|
142
|
-
|
143
|
-
|
147
|
+
# Register a Ruby object with Polar.
|
148
|
+
#
|
149
|
+
# @param value [Object] the object to register.
|
150
|
+
# @param name [String] the name to register the object as.
|
151
|
+
# @return [self] for chaining.
|
152
|
+
# @raise [FFI::Error] if the FFI call returns an error.
|
153
|
+
def register_constant(value, name:)
|
154
|
+
ffi_polar.register_constant(host.to_polar(value), name: name)
|
155
|
+
self
|
144
156
|
end
|
145
157
|
|
146
158
|
# Start a REPL session.
|
@@ -162,10 +174,6 @@ module Oso
|
|
162
174
|
|
163
175
|
# @return [FFI::Polar]
|
164
176
|
attr_reader :ffi_polar
|
165
|
-
# @return [Hash<String, String>]
|
166
|
-
attr_reader :loaded_names
|
167
|
-
# @return [Hash<String, String>]
|
168
|
-
attr_reader :loaded_contents
|
169
177
|
|
170
178
|
# The R and L in REPL for systems where readline is available.
|
171
179
|
def repl_readline(prompt)
|
data/lib/oso/polar/query.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'json'
|
4
|
+
|
3
5
|
module Oso
|
4
6
|
module Polar
|
5
7
|
# A single Polar query.
|
@@ -34,33 +36,6 @@ module Oso
|
|
34
36
|
ffi_query.question_result(result, call_id: call_id)
|
35
37
|
end
|
36
38
|
|
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(attribute, call_id:, instance:, args:, kwargs:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
47
|
-
return if calls.key?(call_id)
|
48
|
-
|
49
|
-
instance = host.to_ruby(instance)
|
50
|
-
args = args.map { |a| host.to_ruby(a) }
|
51
|
-
kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
|
52
|
-
# The kwargs.empty? check is for Ruby < 2.7.
|
53
|
-
result = if kwargs.empty?
|
54
|
-
instance.__send__(attribute, *args)
|
55
|
-
else
|
56
|
-
instance.__send__(attribute, *args, **kwargs)
|
57
|
-
end
|
58
|
-
result = [result].to_enum unless result.is_a? Enumerator # Call must be a generator.
|
59
|
-
calls[call_id] = result.lazy
|
60
|
-
rescue ArgumentError, NoMethodError
|
61
|
-
raise InvalidCallError
|
62
|
-
end
|
63
|
-
|
64
39
|
# Send next result of Ruby method call across FFI boundary.
|
65
40
|
#
|
66
41
|
# @param result [String]
|
@@ -95,13 +70,33 @@ module Oso
|
|
95
70
|
# @param call_id [Integer]
|
96
71
|
# @param instance [Hash<String, Object>]
|
97
72
|
# @raise [Error] if the FFI call raises one.
|
98
|
-
def handle_call(attribute, call_id:, instance:, args:, kwargs:)
|
99
|
-
|
100
|
-
|
73
|
+
def handle_call(attribute, call_id:, instance:, args:, kwargs:) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
74
|
+
instance = host.to_ruby(instance)
|
75
|
+
args = args.map { |a| host.to_ruby(a) }
|
76
|
+
kwargs = Hash[kwargs.map { |k, v| [k.to_sym, host.to_ruby(v)] }]
|
77
|
+
# The kwargs.empty? check is for Ruby < 2.7.
|
78
|
+
result = if kwargs.empty?
|
79
|
+
instance.__send__(attribute, *args)
|
80
|
+
else
|
81
|
+
instance.__send__(attribute, *args, **kwargs)
|
82
|
+
end
|
83
|
+
result = JSON.dump(host.to_polar(result))
|
101
84
|
call_result(result, call_id: call_id)
|
102
|
-
rescue
|
85
|
+
rescue ArgumentError, NoMethodError => e
|
103
86
|
application_error(e.message)
|
104
87
|
call_result(nil, call_id: call_id)
|
88
|
+
end
|
89
|
+
|
90
|
+
def handle_next_external(call_id, iterable)
|
91
|
+
unless calls.key? call_id
|
92
|
+
value = host.to_ruby iterable
|
93
|
+
raise InvalidIteratorError unless value.is_a? Enumerable
|
94
|
+
|
95
|
+
calls[call_id] = value.lazy
|
96
|
+
end
|
97
|
+
|
98
|
+
result = JSON.dump(next_call_result(call_id))
|
99
|
+
call_result(result, call_id: call_id)
|
105
100
|
rescue StopIteration
|
106
101
|
call_result(nil, call_id: call_id)
|
107
102
|
end
|
@@ -169,6 +164,12 @@ module Oso
|
|
169
164
|
end
|
170
165
|
command = JSON.dump(host.to_polar(input))
|
171
166
|
ffi_query.debug_command(command)
|
167
|
+
when 'ExternalOp'
|
168
|
+
raise UnimplementedOperationError, 'comparison operators'
|
169
|
+
when 'NextExternal'
|
170
|
+
call_id = event.data['call_id']
|
171
|
+
iterable = event.data['iterable']
|
172
|
+
handle_next_external(call_id, iterable)
|
172
173
|
else
|
173
174
|
raise "Unhandled event: #{JSON.dump(event.inspect)}"
|
174
175
|
end
|
data/lib/oso/version.rb
CHANGED
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.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oso Security, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ffi
|