immunio 0.15.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +234 -0
- data/README.md +147 -0
- data/bin/immunio +5 -0
- data/lib/immunio.rb +29 -0
- data/lib/immunio/agent.rb +260 -0
- data/lib/immunio/authentication.rb +96 -0
- data/lib/immunio/blocked_app.rb +38 -0
- data/lib/immunio/channel.rb +432 -0
- data/lib/immunio/cli.rb +39 -0
- data/lib/immunio/context.rb +114 -0
- data/lib/immunio/errors.rb +43 -0
- data/lib/immunio/immunio_ca.crt +45 -0
- data/lib/immunio/logger.rb +87 -0
- data/lib/immunio/plugins/action_dispatch.rb +45 -0
- data/lib/immunio/plugins/action_view.rb +431 -0
- data/lib/immunio/plugins/active_record.rb +707 -0
- data/lib/immunio/plugins/active_record_relation.rb +370 -0
- data/lib/immunio/plugins/authlogic.rb +80 -0
- data/lib/immunio/plugins/csrf.rb +24 -0
- data/lib/immunio/plugins/devise.rb +40 -0
- data/lib/immunio/plugins/environment_reporter.rb +69 -0
- data/lib/immunio/plugins/eval.rb +51 -0
- data/lib/immunio/plugins/exception_handler.rb +55 -0
- data/lib/immunio/plugins/gems_tracker.rb +5 -0
- data/lib/immunio/plugins/haml.rb +36 -0
- data/lib/immunio/plugins/http_finisher.rb +50 -0
- data/lib/immunio/plugins/http_tracker.rb +203 -0
- data/lib/immunio/plugins/io.rb +96 -0
- data/lib/immunio/plugins/redirect.rb +42 -0
- data/lib/immunio/plugins/warden.rb +66 -0
- data/lib/immunio/processor.rb +234 -0
- data/lib/immunio/rails.rb +26 -0
- data/lib/immunio/request.rb +139 -0
- data/lib/immunio/rufus_lua_ext/ref.rb +27 -0
- data/lib/immunio/rufus_lua_ext/state.rb +157 -0
- data/lib/immunio/rufus_lua_ext/table.rb +137 -0
- data/lib/immunio/rufus_lua_ext/utils.rb +13 -0
- data/lib/immunio/version.rb +5 -0
- data/lib/immunio/vm.rb +291 -0
- data/lua-hooks/ext/all.c +78 -0
- data/lua-hooks/ext/bitop/README +22 -0
- data/lua-hooks/ext/bitop/bit.c +189 -0
- data/lua-hooks/ext/extconf.rb +38 -0
- data/lua-hooks/ext/libinjection/COPYING +37 -0
- data/lua-hooks/ext/libinjection/libinjection.h +65 -0
- data/lua-hooks/ext/libinjection/libinjection_html5.c +847 -0
- data/lua-hooks/ext/libinjection/libinjection_html5.h +54 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli.c +2301 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli.h +295 -0
- data/lua-hooks/ext/libinjection/libinjection_sqli_data.h +9349 -0
- data/lua-hooks/ext/libinjection/libinjection_xss.c +531 -0
- data/lua-hooks/ext/libinjection/libinjection_xss.h +21 -0
- data/lua-hooks/ext/libinjection/lualib.c +109 -0
- data/lua-hooks/ext/lpeg/HISTORY +90 -0
- data/lua-hooks/ext/lpeg/lpcap.c +537 -0
- data/lua-hooks/ext/lpeg/lpcap.h +43 -0
- data/lua-hooks/ext/lpeg/lpcode.c +986 -0
- data/lua-hooks/ext/lpeg/lpcode.h +34 -0
- data/lua-hooks/ext/lpeg/lpeg-128.gif +0 -0
- data/lua-hooks/ext/lpeg/lpeg.html +1429 -0
- data/lua-hooks/ext/lpeg/lpprint.c +244 -0
- data/lua-hooks/ext/lpeg/lpprint.h +35 -0
- data/lua-hooks/ext/lpeg/lptree.c +1238 -0
- data/lua-hooks/ext/lpeg/lptree.h +77 -0
- data/lua-hooks/ext/lpeg/lptypes.h +149 -0
- data/lua-hooks/ext/lpeg/lpvm.c +355 -0
- data/lua-hooks/ext/lpeg/lpvm.h +58 -0
- data/lua-hooks/ext/lpeg/makefile +55 -0
- data/lua-hooks/ext/lpeg/re.html +498 -0
- data/lua-hooks/ext/lpeg/test.lua +1409 -0
- data/lua-hooks/ext/lua-cmsgpack/CMakeLists.txt +45 -0
- data/lua-hooks/ext/lua-cmsgpack/README.md +115 -0
- data/lua-hooks/ext/lua-cmsgpack/lua_cmsgpack.c +957 -0
- data/lua-hooks/ext/lua-cmsgpack/test.lua +570 -0
- data/lua-hooks/ext/lua-snapshot/LICENSE +7 -0
- data/lua-hooks/ext/lua-snapshot/Makefile +12 -0
- data/lua-hooks/ext/lua-snapshot/README.md +18 -0
- data/lua-hooks/ext/lua-snapshot/dump.lua +15 -0
- data/lua-hooks/ext/lua-snapshot/snapshot.c +455 -0
- data/lua-hooks/ext/lua/COPYRIGHT +34 -0
- data/lua-hooks/ext/lua/lapi.c +1087 -0
- data/lua-hooks/ext/lua/lapi.h +16 -0
- data/lua-hooks/ext/lua/lauxlib.c +652 -0
- data/lua-hooks/ext/lua/lauxlib.h +174 -0
- data/lua-hooks/ext/lua/lbaselib.c +659 -0
- data/lua-hooks/ext/lua/lcode.c +831 -0
- data/lua-hooks/ext/lua/lcode.h +76 -0
- data/lua-hooks/ext/lua/ldblib.c +398 -0
- data/lua-hooks/ext/lua/ldebug.c +638 -0
- data/lua-hooks/ext/lua/ldebug.h +33 -0
- data/lua-hooks/ext/lua/ldo.c +519 -0
- data/lua-hooks/ext/lua/ldo.h +57 -0
- data/lua-hooks/ext/lua/ldump.c +164 -0
- data/lua-hooks/ext/lua/lfunc.c +174 -0
- data/lua-hooks/ext/lua/lfunc.h +34 -0
- data/lua-hooks/ext/lua/lgc.c +710 -0
- data/lua-hooks/ext/lua/lgc.h +110 -0
- data/lua-hooks/ext/lua/linit.c +38 -0
- data/lua-hooks/ext/lua/liolib.c +556 -0
- data/lua-hooks/ext/lua/llex.c +463 -0
- data/lua-hooks/ext/lua/llex.h +81 -0
- data/lua-hooks/ext/lua/llimits.h +128 -0
- data/lua-hooks/ext/lua/lmathlib.c +263 -0
- data/lua-hooks/ext/lua/lmem.c +86 -0
- data/lua-hooks/ext/lua/lmem.h +49 -0
- data/lua-hooks/ext/lua/loadlib.c +705 -0
- data/lua-hooks/ext/lua/loadlib_rel.c +760 -0
- data/lua-hooks/ext/lua/lobject.c +214 -0
- data/lua-hooks/ext/lua/lobject.h +381 -0
- data/lua-hooks/ext/lua/lopcodes.c +102 -0
- data/lua-hooks/ext/lua/lopcodes.h +268 -0
- data/lua-hooks/ext/lua/loslib.c +243 -0
- data/lua-hooks/ext/lua/lparser.c +1339 -0
- data/lua-hooks/ext/lua/lparser.h +82 -0
- data/lua-hooks/ext/lua/lstate.c +214 -0
- data/lua-hooks/ext/lua/lstate.h +169 -0
- data/lua-hooks/ext/lua/lstring.c +111 -0
- data/lua-hooks/ext/lua/lstring.h +31 -0
- data/lua-hooks/ext/lua/lstrlib.c +871 -0
- data/lua-hooks/ext/lua/ltable.c +588 -0
- data/lua-hooks/ext/lua/ltable.h +40 -0
- data/lua-hooks/ext/lua/ltablib.c +287 -0
- data/lua-hooks/ext/lua/ltm.c +75 -0
- data/lua-hooks/ext/lua/ltm.h +54 -0
- data/lua-hooks/ext/lua/lua.c +392 -0
- data/lua-hooks/ext/lua/lua.def +131 -0
- data/lua-hooks/ext/lua/lua.h +388 -0
- data/lua-hooks/ext/lua/lua.rc +28 -0
- data/lua-hooks/ext/lua/lua_dll.rc +26 -0
- data/lua-hooks/ext/lua/luac.c +200 -0
- data/lua-hooks/ext/lua/luac.rc +1 -0
- data/lua-hooks/ext/lua/luaconf.h +763 -0
- data/lua-hooks/ext/lua/luaconf.h.in +724 -0
- data/lua-hooks/ext/lua/luaconf.h.orig +763 -0
- data/lua-hooks/ext/lua/lualib.h +53 -0
- data/lua-hooks/ext/lua/lundump.c +227 -0
- data/lua-hooks/ext/lua/lundump.h +36 -0
- data/lua-hooks/ext/lua/lvm.c +767 -0
- data/lua-hooks/ext/lua/lvm.h +36 -0
- data/lua-hooks/ext/lua/lzio.c +82 -0
- data/lua-hooks/ext/lua/lzio.h +67 -0
- data/lua-hooks/ext/lua/print.c +227 -0
- data/lua-hooks/ext/luautf8/README.md +152 -0
- data/lua-hooks/ext/luautf8/lutf8lib.c +1274 -0
- data/lua-hooks/ext/luautf8/unidata.h +3064 -0
- data/lua-hooks/lib/boot.lua +254 -0
- data/lua-hooks/lib/encode.lua +4 -0
- data/lua-hooks/lib/lexers/LICENSE +21 -0
- data/lua-hooks/lib/lexers/bash.lua +134 -0
- data/lua-hooks/lib/lexers/bash_dqstr.lua +62 -0
- data/lua-hooks/lib/lexers/css.lua +216 -0
- data/lua-hooks/lib/lexers/html.lua +106 -0
- data/lua-hooks/lib/lexers/javascript.lua +68 -0
- data/lua-hooks/lib/lexers/lexer.lua +1575 -0
- data/lua-hooks/lib/lexers/markers.lua +33 -0
- metadata +308 -0
@@ -0,0 +1,139 @@
|
|
1
|
+
require_relative "vm"
|
2
|
+
|
3
|
+
module Immunio
|
4
|
+
class Request
|
5
|
+
# Data sent to the server
|
6
|
+
attr_accessor :data, :vm
|
7
|
+
|
8
|
+
# We keep the VM and hook handlers in the request to ensure all hooks for this request
|
9
|
+
# use the same handlers.
|
10
|
+
attr_accessor :vm
|
11
|
+
|
12
|
+
def initialize(env)
|
13
|
+
@env = env
|
14
|
+
@data = {
|
15
|
+
"type" => "engine.http_request",
|
16
|
+
"request_id" => id,
|
17
|
+
"start_time" => Time.now.utc.iso8601(6),
|
18
|
+
}
|
19
|
+
@timings = Hash.new { |h, name| h[name] = { count: 0, total_duration: 0 } }
|
20
|
+
@paused_times = {}
|
21
|
+
@start_time = Time.now
|
22
|
+
|
23
|
+
Immunio.logger.debug "Created new Request #{id} with data #{@data}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def id
|
27
|
+
@env["action_dispatch.request_id"] || internal_id
|
28
|
+
end
|
29
|
+
|
30
|
+
# Time the execution of a block and stores it in the request's `timings`.
|
31
|
+
def time(type, name)
|
32
|
+
Immunio.logger.debug { "Starting request time for #{type} #{name}" }
|
33
|
+
start = Time.now
|
34
|
+
key = {type: type, name: name}
|
35
|
+
nested = false
|
36
|
+
if @paused_times[key]
|
37
|
+
nested = true
|
38
|
+
else
|
39
|
+
@paused_times[key] = 0
|
40
|
+
end
|
41
|
+
yield
|
42
|
+
ensure
|
43
|
+
timing = @timings[key]
|
44
|
+
timing[:count] += 1
|
45
|
+
timing[:total_duration] += (Time.now - start) * 1000
|
46
|
+
if !nested
|
47
|
+
timing[:total_duration] -= @paused_times.delete(key)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Pause execution timing while executing block
|
52
|
+
def pause(type, name)
|
53
|
+
key = {type: type, name: name}
|
54
|
+
start = Time.now
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
@paused_times[key]+= (Time.now - start) * 1000
|
58
|
+
end
|
59
|
+
|
60
|
+
def timings
|
61
|
+
total_key = {type: "request", name: "total"}
|
62
|
+
timing = @timings[total_key]
|
63
|
+
timing[:count] = 1
|
64
|
+
timing[:total_duration] = (Time.now - @start_time) * 1000
|
65
|
+
|
66
|
+
split_timings = {}
|
67
|
+
@timings.each do |key, value|
|
68
|
+
# Round to microsecond precision
|
69
|
+
value[:total_duration] = value[:total_duration].round(3)
|
70
|
+
|
71
|
+
split_timings[key[:type]] ||= {}
|
72
|
+
split_timings[key[:type]][key[:name]] = value
|
73
|
+
end
|
74
|
+
split_timings
|
75
|
+
end
|
76
|
+
|
77
|
+
def should_report?
|
78
|
+
result = Immunio.run_hook "request", "should_report"
|
79
|
+
# If the hook doesn't exist, turn a nil report value into a false value
|
80
|
+
report = !!result["report"]
|
81
|
+
Immunio.logger.debug "Report request: #{report}"
|
82
|
+
report
|
83
|
+
end
|
84
|
+
|
85
|
+
# Encode the request data to send it to the channel.
|
86
|
+
def encode
|
87
|
+
if @data.is_a? Hash
|
88
|
+
@data.to_msgpack
|
89
|
+
else # Lua table
|
90
|
+
# Encode the data in Lua to avoid pulling a huge Lua table out of the VM, which is slow.
|
91
|
+
@vm.unsafe_call_by_name 'encode', @data
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.current
|
96
|
+
Thread.current["immunio.request"]
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.current=(request)
|
100
|
+
Thread.current["immunio.request"] = request
|
101
|
+
Immunio.logger.debug "Setting current request for thread #{Thread.current.object_id} to #{request && request.id || nil}"
|
102
|
+
end
|
103
|
+
|
104
|
+
# This shim exists to preserve the stack depth into hooks when self.time and self.pause are
|
105
|
+
# called. Otherwise the number of immunio frames will vary based on the state of the request object
|
106
|
+
def self.stack_shim()
|
107
|
+
yield
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.time(type, name, &block)
|
111
|
+
if Request.current
|
112
|
+
Request.current.time(type, name, &block)
|
113
|
+
else
|
114
|
+
Immunio.logger.debug { "Starting request time for #{type} #{name}" }
|
115
|
+
self.stack_shim(&block)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.pause(type, name, &block)
|
120
|
+
if Request.current
|
121
|
+
Request.current.pause(type, name, &block)
|
122
|
+
else
|
123
|
+
self.stack_shim(&block)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def internal_id
|
130
|
+
return @internal_id if defined?(@internal_id)
|
131
|
+
|
132
|
+
if @env['HTTP_X_REQUEST_ID']
|
133
|
+
@internal_id = @env['HTTP_X_REQUEST_ID'].gsub(/[^\w\-]/, '').first(255)
|
134
|
+
else
|
135
|
+
@internal_id = SecureRandom.uuid
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rufus::Lua
|
2
|
+
# Auto-unref Lua ref when it goes out of scope in Ruby.
|
3
|
+
class Ref
|
4
|
+
def initialize_with_finalizer(*args)
|
5
|
+
initialize_without_finalizer(*args)
|
6
|
+
|
7
|
+
ObjectSpace.define_finalizer self, self.class.finalize(@pointer, @ref)
|
8
|
+
end
|
9
|
+
alias_method :initialize_without_finalizer, :initialize
|
10
|
+
alias_method :initialize, :initialize_with_finalizer
|
11
|
+
|
12
|
+
def self.finalize(pointer, ref)
|
13
|
+
# Hack to test if the Lua state was closed and de-refed.
|
14
|
+
original_pointer_address = pointer.read_pointer
|
15
|
+
proc do
|
16
|
+
Lib.luaL_unref(pointer, LUA_REGISTRYINDEX, ref) if original_pointer_address == pointer.read_pointer
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
undef :free
|
21
|
+
def free
|
22
|
+
# Noop. Taken care of by finalizer. Should not conflict.
|
23
|
+
end
|
24
|
+
|
25
|
+
public :load_onto_stack
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
module Rufus::Lua
|
2
|
+
class State
|
3
|
+
# Load the code into a function and return it.
|
4
|
+
# Safer than evaling some string.
|
5
|
+
def load(code, filename=nil)
|
6
|
+
err = Lib.luaL_loadbuffer(@pointer, code, code.size, filename)
|
7
|
+
fail_if_error('load:compile', err, nil, filename, nil)
|
8
|
+
|
9
|
+
function = stack_fetch
|
10
|
+
stack_pop
|
11
|
+
|
12
|
+
function
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class HashWithNilKeyError < StandardError
|
17
|
+
end
|
18
|
+
|
19
|
+
module StateMixin
|
20
|
+
# Expose error_handler to set it manually on Functions since it is not inherited from State.
|
21
|
+
attr_accessor :error_handler
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
# Fix stack_fetch so it doesn't pop a value off the top for tables,
|
26
|
+
# functions, and threads.
|
27
|
+
#
|
28
|
+
# FIXME: pos isn't honored for tables, functions, or threads.
|
29
|
+
undef :stack_fetch
|
30
|
+
def stack_fetch(pos=-1)
|
31
|
+
type, tname = stack_type_at(pos)
|
32
|
+
|
33
|
+
case type
|
34
|
+
|
35
|
+
when TNIL then nil
|
36
|
+
|
37
|
+
when TSTRING then
|
38
|
+
string = nil
|
39
|
+
FFI::MemoryPointer.new(:size_t) do |len|
|
40
|
+
ptr = Lib.lua_tolstring(@pointer, pos, len)
|
41
|
+
string = ptr.read_string(len.read_long)
|
42
|
+
end
|
43
|
+
string
|
44
|
+
|
45
|
+
when TBOOLEAN then (Lib.lua_toboolean(@pointer, pos) == 1)
|
46
|
+
when TNUMBER then Lib.lua_tonumber(@pointer, pos)
|
47
|
+
|
48
|
+
when TTABLE then
|
49
|
+
table = Table.new(@pointer)
|
50
|
+
table.load_onto_stack
|
51
|
+
table
|
52
|
+
|
53
|
+
when TFUNCTION then
|
54
|
+
func = Function.new(@pointer)
|
55
|
+
func.load_onto_stack
|
56
|
+
func
|
57
|
+
|
58
|
+
when TTHREAD then
|
59
|
+
coroutine = Coroutine.new(@pointer)
|
60
|
+
coroutine.load_onto_stack
|
61
|
+
coroutine
|
62
|
+
|
63
|
+
else tname
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Now that we've fixed popping in stack_fetch above, always remove the top
|
68
|
+
# item from the stack.
|
69
|
+
undef :stack_pop
|
70
|
+
def stack_pop
|
71
|
+
r = stack_fetch
|
72
|
+
stack_unstack
|
73
|
+
r
|
74
|
+
end
|
75
|
+
|
76
|
+
# This method is effectively a pop without retrieving the top value.
|
77
|
+
# Upstream papers over stack corruption by not letting the stack pointer
|
78
|
+
# go negative, but this makes our tests erroneously pass when they check
|
79
|
+
# for stack corruption. Instead, do the right thing, and catch problems in
|
80
|
+
# tests.
|
81
|
+
undef :stack_unstack
|
82
|
+
def stack_unstack
|
83
|
+
Lib.lua_settop(@pointer, -2)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Patch `stack_push` to support passing Lua objects (functions, tables)
|
87
|
+
# back into the VM. Also, support conversion of Pathname objects.
|
88
|
+
def stack_push_with_ref(o)
|
89
|
+
case o
|
90
|
+
when Ref
|
91
|
+
o.load_onto_stack
|
92
|
+
when Pathname
|
93
|
+
stack_push_without_ref o.to_s
|
94
|
+
when File
|
95
|
+
stack_push_without_ref o.path
|
96
|
+
when ActiveSupport::Multibyte::Chars
|
97
|
+
stack_push_without_ref o.to_s
|
98
|
+
else
|
99
|
+
begin
|
100
|
+
stack_push_without_ref o
|
101
|
+
rescue ArgumentError
|
102
|
+
# Rufus-lua doesn't know how to handle it, so call inspect which
|
103
|
+
# should always return a string describing the object. Usually we
|
104
|
+
# just need a placeholder for such objects, so this will do.
|
105
|
+
stack_push_without_ref o.inspect
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
alias_method :stack_push_without_ref, :stack_push
|
110
|
+
alias_method :stack_push, :stack_push_with_ref
|
111
|
+
|
112
|
+
# Patch `stack_fetch` to set encoding to strings.
|
113
|
+
def stack_fetch_with_unicode(pos=-1)
|
114
|
+
type = stack_type_at(pos)
|
115
|
+
|
116
|
+
case type[0]
|
117
|
+
when TSTRING
|
118
|
+
string = nil
|
119
|
+
FFI::MemoryPointer.new(:size_t) do |len|
|
120
|
+
ptr = Lib.lua_tolstring(@pointer, pos, len)
|
121
|
+
string = ptr.read_string(len.read_long)
|
122
|
+
end
|
123
|
+
string.force_encoding(Encoding.default_internal)
|
124
|
+
else
|
125
|
+
stack_fetch_without_unicode pos
|
126
|
+
end
|
127
|
+
end
|
128
|
+
alias_method :stack_fetch_without_unicode, :stack_fetch
|
129
|
+
alias_method :stack_fetch, :stack_fetch_with_unicode
|
130
|
+
|
131
|
+
# Trying to push a hash with a nil key causes a Lua panic. Instead, raise
|
132
|
+
# an exception in Ruby which we can deal with.
|
133
|
+
def stack_push_hash_with_immunio(h)
|
134
|
+
if h.has_key? nil
|
135
|
+
raise HashWithNilKeyError.new "can't pass hash with nil key into Lua"
|
136
|
+
end
|
137
|
+
stack_push_hash_without_immunio h
|
138
|
+
end
|
139
|
+
alias_method :stack_push_hash_without_immunio, :stack_push_hash
|
140
|
+
alias_method :stack_push_hash, :stack_push_hash_with_immunio
|
141
|
+
end
|
142
|
+
|
143
|
+
class Function
|
144
|
+
# If an argument fails to be converted for Lua, we end up leaking everything
|
145
|
+
# that was already pushed onto the stack for the call. Make sure we reset
|
146
|
+
# our stack.
|
147
|
+
def call_with_stack_reset(*args)
|
148
|
+
begin
|
149
|
+
call_without_stack_reset(*args)
|
150
|
+
rescue
|
151
|
+
Lib.lua_settop(@pointer, 0)
|
152
|
+
raise
|
153
|
+
end
|
154
|
+
end
|
155
|
+
alias_method_chain :call, :stack_reset
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Rufus::Lua
|
2
|
+
class Table
|
3
|
+
# Method to behave like Hash#fetch.
|
4
|
+
def fetch(key, default=nil)
|
5
|
+
value = self[key]
|
6
|
+
if value.nil?
|
7
|
+
default
|
8
|
+
else
|
9
|
+
value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_key?(key)
|
14
|
+
self.fetch(key) != nil
|
15
|
+
end
|
16
|
+
|
17
|
+
# Fix memory leak
|
18
|
+
undef :"[]"
|
19
|
+
def [](k)
|
20
|
+
# Push table onto stack
|
21
|
+
load_onto_stack
|
22
|
+
|
23
|
+
# Push key onto stack
|
24
|
+
stack_push(k)
|
25
|
+
|
26
|
+
# Fetches the value from the table, replaces the key at the top of the
|
27
|
+
# stack with the value. At the end, the stack still has the table at -2.
|
28
|
+
Lib.lua_gettable(@pointer, -2)
|
29
|
+
|
30
|
+
# Pop value off stack
|
31
|
+
v = stack_pop
|
32
|
+
|
33
|
+
# Pop table off stack
|
34
|
+
stack_pop # <= Memory leak fixed here
|
35
|
+
|
36
|
+
v
|
37
|
+
end
|
38
|
+
|
39
|
+
# Fix memory leak
|
40
|
+
undef :"[]="
|
41
|
+
def []=(k, v)
|
42
|
+
# Push table onto stack
|
43
|
+
load_onto_stack
|
44
|
+
|
45
|
+
stack_push(k)
|
46
|
+
stack_push(v)
|
47
|
+
|
48
|
+
# For the VM heavy thread test, we need to cause out-of-order execution in
|
49
|
+
# methods modifying the Lua stack. Here we tell Ruby to swap out threads
|
50
|
+
# if we are in the thread test.
|
51
|
+
Thread.pass if $IMMUNIO_IN_THREAD_TEST
|
52
|
+
|
53
|
+
# Sets the key-value pair onto the table, then pops the key and value but
|
54
|
+
# leaves the table on the stack
|
55
|
+
Lib.lua_settable(@pointer, -3)
|
56
|
+
|
57
|
+
# Pop table off stack
|
58
|
+
stack_pop # <= Memory leak fixed here
|
59
|
+
|
60
|
+
v
|
61
|
+
end
|
62
|
+
|
63
|
+
# Fix memory leak
|
64
|
+
undef :objlen
|
65
|
+
def objlen
|
66
|
+
# Push table onto stack
|
67
|
+
load_onto_stack
|
68
|
+
|
69
|
+
# Gets the length of the table, but leaves the table on the stack
|
70
|
+
len = Lib.lua_objlen(@pointer, -1)
|
71
|
+
|
72
|
+
# Pop table off stack
|
73
|
+
stack_pop # <= Memory leak fixed here
|
74
|
+
|
75
|
+
len
|
76
|
+
end
|
77
|
+
|
78
|
+
# Override original to_h to fix bad implementation and convert nested
|
79
|
+
# collections.
|
80
|
+
# See http://www.lua.org/manual/5.1/manual.html#lua%5Fnext for details on
|
81
|
+
# this crazy complex iteration system in lua...
|
82
|
+
undef :to_h
|
83
|
+
def to_h
|
84
|
+
# Load table onto stack
|
85
|
+
load_onto_stack
|
86
|
+
|
87
|
+
# Get position of table on stack
|
88
|
+
table_pos = stack_top
|
89
|
+
|
90
|
+
# This is the starter for the lua_next iterator. Push a nil value as a
|
91
|
+
# sentinel onto the stack. This tells lua_next to fetch the first key-
|
92
|
+
# value pair from the table.
|
93
|
+
Lib.lua_pushnil(@pointer)
|
94
|
+
|
95
|
+
h = {}
|
96
|
+
|
97
|
+
# Fetch the next key-value pair. The last key must be on the top of the
|
98
|
+
# stack. The key on the stack will be overwritten with the next key
|
99
|
+
# fetched from the table. The value will be pushed onto the stack after
|
100
|
+
# the key.
|
101
|
+
#
|
102
|
+
# In order to iterate, the caller must pop the value off the stack.
|
103
|
+
# lua_next then uses the current key, which is once again at the top of
|
104
|
+
# the stack, to locate the next key.
|
105
|
+
#
|
106
|
+
# But! When there are no more key-value pairs, this call will remove the
|
107
|
+
# key from the stack, leaving the table at the top.
|
108
|
+
#
|
109
|
+
# WARNING: Lua keys can be anything (yes, that includes a table). We do
|
110
|
+
# not support tables as keys. Keys must be nice numbers or strings.
|
111
|
+
while Lib.lua_next(@pointer, table_pos) != 0 do
|
112
|
+
# Grab key and value from top of stack
|
113
|
+
value = stack_fetch(-1)
|
114
|
+
key = stack_fetch(-2)
|
115
|
+
|
116
|
+
# Pop the value off the stack leaving the key on top
|
117
|
+
stack_unstack
|
118
|
+
|
119
|
+
# Recurse into table if needed
|
120
|
+
if Table === value
|
121
|
+
if value[1]
|
122
|
+
h[key] = value.to_a
|
123
|
+
else
|
124
|
+
h[key] = value.to_h
|
125
|
+
end
|
126
|
+
else
|
127
|
+
h[key] = value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# Remove the table pushed onto the stack at the very beginning
|
132
|
+
Lib.lua_remove(@pointer, table_pos)
|
133
|
+
|
134
|
+
h
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|