police-dataflow 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source :rubygems
2
+
3
+ group :development do
4
+ gem 'bundler', '>= 1.1.0'
5
+ gem 'jeweler', '>= 1.8.3'
6
+ gem 'minitest', '>= 2.11.2'
7
+ gem 'rdoc', '>= 3.12'
8
+ gem 'simplecov', '>= 0.6.1'
9
+ gem 'yard', '>= 0.7'
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,31 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.8.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rdoc
10
+ json (1.6.6)
11
+ minitest (2.11.4)
12
+ multi_json (1.2.0)
13
+ rake (0.9.2.2)
14
+ rdoc (3.12)
15
+ json (~> 1.4)
16
+ simplecov (0.6.1)
17
+ multi_json (~> 1.0)
18
+ simplecov-html (~> 0.5.3)
19
+ simplecov-html (0.5.3)
20
+ yard (0.7.5)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (>= 1.1.0)
27
+ jeweler (>= 1.8.3)
28
+ minitest (>= 2.11.2)
29
+ rdoc (>= 3.12)
30
+ simplecov (>= 0.6.1)
31
+ yard (>= 0.7)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Massachusetts Institute of Technology
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,9 @@
1
+ # police-dataflow
2
+
3
+ Pure Ruby implementation of data flow label propagation.
4
+
5
+
6
+ ## Copyright
7
+
8
+ Copyright (c) 2012 Massachusetts Institute of Technology. See LICENSE.txt for
9
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "police-dataflow"
18
+ gem.homepage = "http://github.com/csail/police"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Data flow label propagation}
21
+ gem.description = %Q{Pure Ruby implementtion of data flow label propagation.}
22
+ gem.email = "victor@costan.us"
23
+ gem.authors = ["Victor Costan"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ task :default => :test
36
+
37
+ require 'yard'
38
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,2 @@
1
+ # Allow require 'police-dataflow' instead of 'police/dataflow'
2
+ require 'police/dataflow'
@@ -0,0 +1,16 @@
1
+ module Police
2
+
3
+ # Data flow labels "track" data as it is processed in a complex system.
4
+ module DataFlow
5
+ end # namespace Police::DataFlow
6
+
7
+ end # namespace Police
8
+
9
+
10
+ require 'police/dataflow/core_extensions.rb'
11
+ require 'police/dataflow/guarding.rb'
12
+ require 'police/dataflow/label.rb'
13
+ require 'police/dataflow/labeling.rb'
14
+ require 'police/dataflow/proxies.rb'
15
+ require 'police/dataflow/proxy_base.rb'
16
+ require 'police/dataflow/proxying.rb'
@@ -0,0 +1,12 @@
1
+ # @private
2
+ # The methods below are used by the Police::Dataflow implementation, and
3
+ # should not be called directly.
4
+ class Object
5
+ # Counterpart to the Police::DataFlow::ProxyBase#__police_labels__ attribute.
6
+ #
7
+ # @return [NilClass] nil, to help the Police::DataFlow implementation
8
+ # distinguish between "real" objects and label-carrying proxies
9
+ def __police_labels__
10
+ nil
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Police
2
+
3
+ module DataFlow
4
+ # TODO(pwnall): spec
5
+ def self.guard
6
+
7
+ end
8
+
9
+ # Guarding logic.
10
+ module Guarding
11
+
12
+ end # namespace Police::DataFlow::Guarding
13
+
14
+ end # namespace Police::DataFlow
15
+
16
+ end # namespace Police
@@ -0,0 +1,86 @@
1
+ module Police
2
+
3
+ module DataFlow
4
+
5
+ # Superclass for objects used as data flow labels.
6
+ class Label
7
+ # True for labels that automatically propagate across operations.
8
+ #
9
+ # Labels that indicate privacy auto-flow. For example, an auto-generated
10
+ # message that contains a user's phone number is just as sensitive as the
11
+ # phone number.
12
+ #
13
+ # Labels that indicate sanitization do not auto-flow. For example, a substring
14
+ # of an HTML-sanitized string is not necessarily HTML-sanitized.
15
+ #
16
+ # @return [Boolean] if true, the label will be automatically added to objects
17
+ # whose value is likely to be derived from other labeled objects
18
+ def self.autoflow?
19
+ true
20
+ end
21
+
22
+ # Label method changing the return value of a method in a labeled object.
23
+ #
24
+ # @param [Symbol] method_name the name of the method that will be decorated
25
+ # by the label
26
+ # @return [Symbol, NilClass] the name of a label instance method that will
27
+ # be given a chance to label the decorated method's return value
28
+ #
29
+ # @see Police::DataFlow::Label.sample_return_hook
30
+ def self.return_hook(method_name)
31
+ :sample_return_hook
32
+ end
33
+
34
+ # Label method changing the values yielded by a method in a labeled object.
35
+ #
36
+ # @param [Symbol] method_name the name of the method that will be decorated
37
+ # by the label
38
+ # @return [Symbol, NilClass] the name of a label instance method that will
39
+ # be given a chance to label the values yielded by the decorated method
40
+ # to its block
41
+ #
42
+ # @see Police::DataFlow::Label.sample_yield_args_hook
43
+ def self.yield_args_hook(method_name)
44
+ :sample_yield_args_hook
45
+ end
46
+
47
+ # Hook that can label a decorated method's return value.
48
+ #
49
+ # @param [Object] value the decorated method's return value; if a method is
50
+ # decorated by multiple labels, the value might be already labeled by
51
+ # another label's return hook
52
+ # @param [Object] receiver the object that the decorated method was called on
53
+ # @param [Array] args the arguments passed to the decorated method
54
+ # @return [Object] either the un-modified value argument, or the return value
55
+ # of calling Police::Dataflow.label on the value argument
56
+ def sample_return_hook(value, receiver, *args)
57
+ Police::DataFlow.label value, self
58
+ end
59
+
60
+ # Hook that can label the values that a decorated method yields to its block.
61
+ #
62
+ # @param [Object] receiver the object that the decorated method was called on
63
+ # @param [Array] yield_args the arguments yielded by the decorated method to
64
+ # its block; the array's elements can be replaced with the return values
65
+ # of calling Police::DataFlow.label on them; if a method is
66
+ # decorated by multiple labels, the values might be already labeled by
67
+ # another label's yield values hook
68
+ # @param [Array] args the arguments passed to the decorated method
69
+ def sample_yield_args_hook(receiver, yield_args, *args)
70
+ yield_args.map! { |arg| Police::DataFlow.label arg, self }
71
+ end
72
+
73
+ # An opportunity for a label to reject being attached to a piece of data.
74
+ #
75
+ # @param [Object] data the data that this label will be attached to
76
+ # @return [Boolean] true if this label can be used with the given piece of
77
+ # data; if this method returns false, the labeling code will raise an
78
+ # exception
79
+ def accepts?(data)
80
+ true
81
+ end
82
+ end # module Police::DataFlow::Label
83
+
84
+ end # namespace Police::DataFlow
85
+
86
+ end # namespace Police
@@ -0,0 +1,71 @@
1
+ module Police
2
+
3
+ module DataFlow
4
+ # Attaches a label to a piece of data.
5
+ #
6
+ # @param [Object] data the data that will be labeled
7
+ # @param [Police::DataFlow::Label] label the label to be applied to the object
8
+ # @return [BasicObject] either the given piece of data, or a proxy that should
9
+ # be used instead of it
10
+ def self.label(data, label)
11
+ label_set = data.__police_labels__
12
+ if label_set.nil?
13
+ proxied = data
14
+ label_set = {}
15
+ autoflow_set = {}
16
+ else
17
+ proxied = data.__police_proxied__
18
+ autoflow_set = data.__police_autoflows__
19
+ end
20
+
21
+ if Police::DataFlow::Labeling.add_label_to_set label, label_set,
22
+ autoflow_set
23
+ data = Police::DataFlow::Proxying.proxy proxied, label_set, autoflow_set
24
+ data
25
+ end
26
+ data
27
+ end
28
+
29
+ # All the labels attached to a piece of data.
30
+ #
31
+ # @param [Object] data the data whose labels are queried
32
+ # @return [Array<Police::DataFlow::Label>] all the labels attached to the data
33
+ def self.labels(data)
34
+ return [] unless label_set = data.__police_labels__
35
+ return label_set.first.last.keys if label_set.length == 1
36
+
37
+ labels = []
38
+ label_set.each { |label_key, label_hash| labels.concat label_hash.keys }
39
+ labels
40
+ end
41
+
42
+ # Label algebra.
43
+ module Labeling
44
+ # Adds a label to the set of labels held by an object's proxy.
45
+ #
46
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] label_set the
47
+ # set of all labels that will be held by the object's proxy
48
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] autoflow_set
49
+ # the set of labels whose autoflow? method returned true
50
+ # @return [Boolean] false if the set already had a label of the same type, so
51
+ # the proxy holding the set can still be used; true if the set had to be
52
+ # expanded, so a new proxy is needed
53
+ def self.add_label_to_set(label, label_set, autoflow_set)
54
+ label_class = label.class
55
+ label_key = label_class.__id__
56
+ if label_set.has_key? label_key
57
+ label_set[label_key][label] = true
58
+ # NOTE: autoflow_set uses use the same hash, so no work is necessary
59
+ return false
60
+ end
61
+
62
+ label_entry = { label => true }
63
+ label_set[label_key] = label_entry
64
+ autoflow_set[label_key] = label_entry if label_class.autoflow?
65
+ true
66
+ end
67
+ end # namespace Police::DataFlow::Labeling
68
+
69
+ end # namespace Police::DataFlow
70
+
71
+ end # namespace Police
@@ -0,0 +1,51 @@
1
+ module Police
2
+
3
+ module DataFlow
4
+
5
+ # Namespace for the auto-generated proxy classes.
6
+ module Proxies
7
+ # A class whose instances proxy instances of a Ruby class.
8
+ #
9
+ # @param [Class] proxied_class the class whose instances will be proxied by
10
+ # instances of the returned class
11
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] label_set the
12
+ # set of all labels that will be carried by the proxied object
13
+ # @return [Class] a Police::DataFlow::ProxyBase subclass that can proxy
14
+ # instances of the given class
15
+ def self.for(proxied_class, label_set)
16
+ unless class_cache = @classes[proxied_class]
17
+ class_cache = {}
18
+ @classes[proxied_class] = class_cache
19
+ end
20
+
21
+ cache_key = label_set.keys.sort!.freeze
22
+ return class_cache[cache_key] if class_cache.has_key? cache_key
23
+
24
+ label_classes = label_set.map { |label_key, label_hash|
25
+ label_hash.first.first.class
26
+ }.sort_by!(&:__id__).freeze
27
+
28
+ proxy_class = Class.new Police::DataFlow::ProxyBase
29
+ proxy_class.__police_classes__ = label_classes
30
+ class_cache[cache_key] = proxy_class
31
+ proxy_class
32
+ end
33
+
34
+ # Clears the cache of proxy classes associated with Ruby classes.
35
+ #
36
+ # This method has a terrible impact on VM performance, and is only intended
37
+ # for testing the Police::DataFlow implementation.
38
+ #
39
+ # @return [Boolean] true
40
+ def self.clear_cache
41
+ @classes.clear
42
+ true
43
+ end
44
+
45
+ # Maps Ruby classes to auto-generated proxy classes.
46
+ @classes = {}
47
+ end # namespace Police::DataFlow::Proxies
48
+
49
+ end # namespace Police::DataFlow
50
+
51
+ end # namespace Police
@@ -0,0 +1,132 @@
1
+ module Police
2
+
3
+ module DataFlow
4
+
5
+ # Base class for labeled objects replacements.
6
+ class ProxyBase < BasicObject
7
+ # The object being proxied by this object.
8
+ #
9
+ # @private
10
+ # Use the Police::DataFlow API instead of reading this attribute directly.
11
+ attr_reader :__police_proxied__
12
+
13
+ # The Label instances attached to the proxied object.
14
+ #
15
+ # @private
16
+ # Use the Police::DataFlow API instead of reading this attribute directly.
17
+ attr_reader :__police_labels__
18
+
19
+ # The subset of this object's labels whose autoflow? method returns true.
20
+ #
21
+ # @private
22
+ # This is an optimization used by the Police::DataFlow implementation. Do not
23
+ # read it directly.
24
+ attr_reader :__police_autoflows__
25
+
26
+ # Creates a proxied object.
27
+ #
28
+ # @param [Object] proxied the object that will receive messages sent to the
29
+ # newly created proxy
30
+ # @param [Class<Police::DataFlow::ProxyBase>] proxy_class the
31
+ # Police::DataFlow::ProxyBase subclass being instantiated; Object
32
+ # instances can call Object#class to get to their class, but BasicObject
33
+ # instances don't have this luxury
34
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] label_set the
35
+ # set of all labels that will be held by the object's proxy
36
+ # @param [Hash<Integer,Hash<Police::DataFlow::Label,Boolean>>] autoflow_set
37
+ # the set of labels whose autoflow? method returned true
38
+ def initialize(proxied, proxy_class, label_set, autoflow_set)
39
+ @__police_proxied__ = proxied
40
+ @__police_labels__ = label_set
41
+
42
+ # Holds the object's class, because Object#class is not available.
43
+ @__police_class__ = proxy_class
44
+
45
+ # Labels that flow automatically across method calls.
46
+ @__police_autoflows__ = autoflow_set
47
+ end
48
+
49
+ # Handles method calls to the proxied object.
50
+ #
51
+ # Whenever possible, proxy methods are created on-the-fly, so that future
52
+ # calls to the same method will be faster.
53
+ def method_missing(name, *args, &block)
54
+ # Build a fast path for future method calls, if possible.
55
+ respond_to_missing? name, true
56
+
57
+ if block
58
+ return_value = @__police_proxied__.__send__ name, *args do |*yield_args|
59
+ # Yielded values filtering.
60
+ @__police_labels__.each do |_, label_hash|
61
+ next unless hook = label_hash.first.first.class.yield_args_hook(name)
62
+ label_hash.each do |label, _|
63
+ block_args = label.__send__ hook, self, yield_args, *args
64
+ end
65
+ end
66
+
67
+ yield_return = yield(*yield_args)
68
+ # TODO(pwnall): consider adding a yield value filter
69
+ next yield_return
70
+ end
71
+ else
72
+ return_value = @__police_proxied__.__send__ name, *args, &block
73
+ end
74
+
75
+ # Return value filtering.
76
+ @__police_labels__.each do |_, label_hash|
77
+ next unless hook = label_hash.first.first.class.return_hook(name)
78
+ label_hash.each do |label, _|
79
+ return_value = label.__send__ hook, return_value, self, *args
80
+ end
81
+ end
82
+ return return_value
83
+ end
84
+
85
+ # Called when Object#respond_to? returns false.
86
+ def respond_to_missing?(name, include_private)
87
+ return false unless @__police_proxied__.respond_to? name, include_private
88
+
89
+ # A method on the proxied object doesn't have a corresponding proxy.
90
+ # Fix this by creating all possible proxies.
91
+
92
+ # NOTE: this approach is cheaper than creating proxies one by one, because
93
+ # it plays nice with method caches
94
+
95
+ ::Police::DataFlow::Proxying.add_class_methods @__police_class__,
96
+ @__police_proxied__.class
97
+
98
+ # NOTE: we don't want to create unnecessary singleton classes
99
+ # target_methods = @__police_proxied__.singleton_methods true
100
+ # unless target_methods.empty?
101
+ # ::Police::DataFlow::Proxying.add_singleton_methods self,
102
+ # @__police_proxied__, target_methods
103
+ # end
104
+
105
+ true
106
+ end
107
+
108
+ # Ruby 1.9 throws scary warnings if proxies define object_id.
109
+ # In either case, it's probably best to have it match __id__.
110
+ alias object_id __id__
111
+
112
+ # Remove the == and != implementations from BasicObject, so that we can proxy
113
+ # them. This is particularly important for String.
114
+ #
115
+ # NOTE: We don't remove equal?, because Object's documentation says that
116
+ # BasicObject subclasses should really not override it. We also don't
117
+ # remove instance_eval and instance_exec, so the code that gets executed
118
+ # using them will still have its method calls proxied correctly.
119
+ undef ==, !=
120
+
121
+ class <<self
122
+ # The classes of the labels supported by the proxy class.
123
+ #
124
+ # @private
125
+ # This is a Police::DataFlow implementation detail. Do not read it directly.
126
+ attr_accessor :__police_classes__
127
+ end
128
+ end # class Police::DataFlow::ProxyBase
129
+
130
+ end # namespace Police::DataFlow
131
+
132
+ end # namespace Police