immunio 0.15.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 +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
|