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 +5 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +31 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +9 -0
- data/Rakefile +38 -0
- data/VERSION +1 -0
- data/lib/police-dataflow.rb +2 -0
- data/lib/police/dataflow.rb +16 -0
- data/lib/police/dataflow/core_extensions.rb +12 -0
- data/lib/police/dataflow/guarding.rb +16 -0
- data/lib/police/dataflow/label.rb +86 -0
- data/lib/police/dataflow/labeling.rb +71 -0
- data/lib/police/dataflow/proxies.rb +51 -0
- data/lib/police/dataflow/proxy_base.rb +132 -0
- data/lib/police/dataflow/proxying.rb +179 -0
- data/police-dataflow.gemspec +62 -0
- data/test/dataflow/core_extensions_test.rb +51 -0
- data/test/dataflow/labeling_test.rb +74 -0
- data/test/dataflow/proxies_test.rb +40 -0
- data/test/dataflow/proxy_base_test.rb +145 -0
- data/test/dataflow/proxying_test.rb +287 -0
- data/test/helper.rb +23 -0
- data/test/helpers/auto_flow_fixture.rb +6 -0
- data/test/helpers/no_flow_fixture.rb +14 -0
- data/test/helpers/proxying_fixture.rb +45 -0
- metadata +76 -0
data/.document
ADDED
data/Gemfile
ADDED
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
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,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,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
|