diff --git a/.travis.yml b/.travis.yml index 32da8de..221baeb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ before_script: - sudo luarocks install lua-cjson - sudo luarocks install luaposix - sudo luarocks install lunit + - sudo luarocks install luasec - cd .. script: diff --git a/README.md b/README.md index d870827..fc6f4b3 100644 --- a/README.md +++ b/README.md @@ -59,14 +59,20 @@ See docs/index.html for more details. Prerequisites ============= +To run the tests: ``` - #for unit tests - $luarocks install lunit - $luarocks install luaposix - $luarocks install luasocket - $luarocks install lua-cjson - - #for generating docs - $luarocks install ldoc + luarocks install lunit + luarocks install lua-cjson + luarocks install luaposix + luarocks install luasocket + luarocks install luasec + + lunit tests/*.lua +``` + +To generate the docs: ``` + luarocks install ldoc + ldoc raven.lua -d docs -p raven-lua +``` diff --git a/docs/index.html b/docs/index.html index fbfa7ef..6ca12a4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -24,7 +24,7 @@
-generated by LDoc 1.4.2 +generated by LDoc 1.4.6 +Last updated 2017-05-16 12:19:18
diff --git a/docs/ldoc.css b/docs/ldoc.css index 765c710..52c4ad2 100644 --- a/docs/ldoc.css +++ b/docs/ldoc.css @@ -28,7 +28,6 @@ del,ins { text-decoration: none; } li { - list-style: disc; margin-left: 20px; } caption,th { @@ -71,7 +70,7 @@ body { background-color: #ffffff; margin: 0px; } -code, tt { font-family: monospace; } +code, tt { font-family: monospace; font-size: 1.1em; } span.parameter { font-family:monospace; } span.parameter:after { content:":"; } span.types:before { content:"("; } @@ -88,7 +87,7 @@ em { font-style: italic;} h1 { font-size: 1.5em; - margin: 0 0 20px 0; + margin: 20px 0 20px 0; } h2, h3, h4 { margin: 15px 0 10px 0; } h2 { font-size: 1.25em; } @@ -114,24 +113,18 @@ p.name { padding-top: 1em; } -pre.example { - background-color: rgb(245, 245, 245); - border: 1px solid silver; - padding: 10px; - margin: 10px 0 10px 0; - font-family: "Andale Mono", monospace; - font-size: .85em; -} - pre { background-color: rgb(245, 245, 245); - border: 1px solid silver; + border: 1px solid #C0C0C0; /* silver */ padding: 10px; margin: 10px 0 10px 0; overflow: auto; font-family: "Andale Mono", monospace; } +pre.example { + font-size: .85em; +} table.index { border: 1px #00007f; } table.index td { text-align: left; vertical-align: top; } @@ -159,7 +152,7 @@ table.index td { text-align: left; vertical-align: top; } #navigation { float: left; - width: 18em; + width: 14em; vertical-align: top; background-color: #f0f0f0; overflow: visible; @@ -193,7 +186,7 @@ table.index td { text-align: left; vertical-align: top; } } #content { - margin-left: 18em; + margin-left: 14em; padding: 1em; width: 700px; border-left: 2px solid #cccccc; @@ -286,17 +279,25 @@ ol ul { margin-top: 0px; } ol ol { margin-top: 0px; } ul ol { margin-top: 0px; } +/* make the target distinct; helps when we're navigating to a function */ +a:target + * { + background-color: #FF9; +} + + /* styles for prettification of source */ pre .comment { color: #558817; } pre .constant { color: #a8660d; } pre .escape { color: #844631; } -pre .keyword { color: #2239a8; font-weight: bold; } +pre .keyword { color: #aa5050; font-weight: bold; } pre .library { color: #0e7c6b; } pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } -pre .string { color: #a8660d; } +pre .string { color: #8080ff; } pre .number { color: #f8660d; } pre .operator { color: #2239a8; font-weight: bold; } pre .preprocessor, pre .prepro { color: #a33243; } pre .global { color: #800080; } +pre .user-keyword { color: #800080; } pre .prompt { color: #558817; } pre .url { color: #272fc2; text-decoration: underline; } + diff --git a/raven.lua b/raven.lua index c774c46..6fe5024 100644 --- a/raven.lua +++ b/raven.lua @@ -19,6 +19,7 @@ -- @author Jiale Zhi -- @copyright (c) 2013-2014, CloudFlare, Inc. -------------------------------------------------------------------- + --pcall(require("luacov")) local json = require("cjson") local debug = require("debug") @@ -44,12 +45,17 @@ local table_insert = table.insert local debug = false local socket +local ssl local catcher_trace_level = 4 if not ngx then local ok, luasocket = pcall(require, "socket") if not ok then error("No socket library found, you need ngx.socket or luasocket.") end + local ok, luassl = pcall(require, "ssl") + if ok then + ssl = luassl + end socket = luasocket else socket = ngx.socket @@ -162,7 +168,7 @@ local function _parse_host_port(protocol, host) local i = string_find(host, ":") if not i then -- TODO - return host, 80 + return host, nil end local port_str = string_sub(host, i + 1) @@ -194,7 +200,7 @@ local function _parse_dsn(dsn, obj) local host, port, err = _parse_host_port(obj.protocol, obj.long_host) - if not host or not port then + if not host then return nil, err end @@ -214,11 +220,18 @@ _M._parse_dsn = _parse_dsn --- Create a new Sentry client. Three parameters: -- @param self raven client -- @param dsn The DSN of the Sentry instance with this format: ---
{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{HOST}/{PATH}{PROJECT_ID}
+--
{PROTOCOL}://{PUBLIC_KEY}:{SECRET_KEY}@{\HOST}/{PATH}{PROJECT_ID}
--
http://pub:secret@127.0.0.1:8080/sentry/proj-id
--- @param conf client configuration. Conf should be a hash table. Possiable --- keys are: "tags", "logger". For example: ---
{ tags = { foo = "bar", abc = "def" }, logger = "myLogger" }
+-- @param conf client configuration. Conf should be a hash table. Possible keys are: +-- +-- For example: +--
{ tags = { foo = "bar", abc = "def" }, logger = "myLogger", verify_ssl = false }
-- @return a new raven instance -- @usage -- local raven = require "raven" @@ -239,6 +252,7 @@ function _M.new(self, dsn, conf) obj.client_id = "raven-lua/" .. version -- default level "error" obj.level = "error" + obj.verify_ssl = true if conf then if conf.tags then @@ -248,6 +262,13 @@ function _M.new(self, dsn, conf) if conf.logger then obj.logger = conf.logger end + + if conf.verify_ssl == false then + obj.verify_ssl = false + end + + + obj.cafile = conf.cafile end return setmetatable(obj, mt) @@ -389,8 +410,10 @@ function _M.send_report(self, json, conf) local json_str = json_encode(json) local ok, err - if self.protocol == "http" then - ok, err = self:http_send(json_str) + if self.protocol == "https" then + ok, err = self:http_send(json_str, true) + elseif self.protocol == "http" then + ok, err = self:http_send(json_str, false) else error("protocol not implemented yet: " .. self.protocol) end @@ -522,23 +545,84 @@ function _M.http_send_core(self, json_str) return string_sub(res, s2 + 1) end +-- lua_wrap_tls: Enables TLS for luasocket. Requires luasec. +function _M.lua_wrap_tls(self, sock) + if not ssl then + error("no ssl library found, please install luasec") + end + + local ok, err + + sock, err = ssl.wrap(sock, { + mode = "client", + protocol = "tlsv1_2", + verify = self.verify_ssl and "peer" or "none", + cafile = self.verify_ssl and self.cafile or nil, + options = "all", + }) + if not sock then + return nil, err + end + + ok, err = sock:dohandshake() + if not ok then + return nil, err + end + + return sock +end + +-- ngx_wrap_tls: Enables TLS for ngx.socket +function _M.ngx_wrap_tls(self, sock) + local session, err = sock:sslhandshake(false, self.host, self.verify_ssl) + if not session then + return nil, err + end + return sock +end + +-- wrap_tls: Wraps a connected socket with TLS +if ngx then + _M.wrap_tls = _M.ngx_wrap_tls +else + _M.wrap_tls = _M.lua_wrap_tls +end + -- http_send: actually sends the structured data to the Sentry server using --- HTTP -function _M.http_send(self, json_str) +-- HTTP or HTTPS +function _M.http_send(self, json_str, secure) local ok, err local sock + local port = self.port sock, err = socket.tcp() if not sock then return nil, err end - self.sock = sock - ok, err = sock:connect(self.host, self.port) + -- Rely on default port values for http and https + if not port then + port = secure and 443 or 80 + end + + ok, err = sock:connect(self.host, port) if not ok then return nil, err end + if secure then + -- Sprinkle on some TLS juice + local tlssock, err = self:wrap_tls(sock) + if not tlssock then + -- Need to close the tcp connection yet before bailing + sock:close() + return nil, err + end + sock = tlssock + end + + self.sock = sock + ok, err = self:http_send_core(json_str) sock:close() diff --git a/tests/certs/ca.cert.pem b/tests/certs/ca.cert.pem new file mode 100644 index 0000000..74ab57c --- /dev/null +++ b/tests/certs/ca.cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFdzCCA1+gAwIBAgIJAN2Ky4KyayS9MA0GCSqGSIb3DQEBCwUAMFExCzAJBgNV +BAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMRgwFgYDVQQKDA9NeSBGYWtlIENB +IEx0ZC4xEzARBgNVBAMMCmZha2VjYS5uZXQwIBcNMTcwNTEyMTUwNjIwWhgPNDc1 +NTA0MDkxNTA2MjBaMFExCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRl +MRgwFgYDVQQKDA9NeSBGYWtlIENBIEx0ZC4xEzARBgNVBAMMCmZha2VjYS5uZXQw +ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCd7tKRCUQyOkB1ZooZ6FQg +uHRBPCa5tL3bt5lsHgzbvTTOaEgzGlAlJHcL/DEwWVzKmVwDSdUjydW/K+O3EEb3 +na6xOWmQxg6SJ4fI4h6lHywxXacWDZs9FBMF9PoNvzCcbbk6c78pvaP9EinvIl8J +o+5jXC1ExQTEkquczZn97RYayQnCYp64WxX4K9LTxhNoozJ/Fnnu/A0HGGzpwvmI +vgaqX+pnSN04PxVQwgWg+TXXzp0OBvennXCzODg8ck/WFokgT6xVKDStuNn1NLrL +R77emnnXkrs0HnTZnyTrRDvytTSx4RiBYLkh6HrGQyg8nhMXmikyIUNa0FFxCK5l +NAlYSMRw5kUJ7n9WbK+Y9MtmutyQt8HoafTz+9rNt3ap2ru0rHBLW1RmpYCJTQuf +V6vJo9LBOUN29H79mBNe/0Lgfk2UVGcTQ7LyXmV/mE4vDVycbMYRki+2qZkkkdHx +SliqjLdgIEJQgVMN1fIXhBoVBNnCxYrr6fl8rFWrD/EANFSOwof2TBUB6EMJsL+g +qdRD1DixmaM5asG9vIjAkFbXF9bEM7ntJwlMz27qXmh3bZTEIm1gRvkoiS3chDyU +9XkihYsGZjhx1kmC0w+DjNCkQFfWCYM7b+Mzv9viVnUsdsCTGOn0Jig9dAVEFih7 +3t/eWRcnrFFYCJhAtXTdAQIDAQABo1AwTjAdBgNVHQ4EFgQUpgxKwJHIRVXVBy7B +osjsVJSzfdEwHwYDVR0jBBgwFoAUpgxKwJHIRVXVBy7BosjsVJSzfdEwDAYDVR0T +BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAPFfztuRxZw3ZFn4WZ/rhEEJRbgNA +uML/2uzRNKjKsBctlOpM6ZAwEqZF1g9gxnPTUEGCICgUrlqxygeyRzwWTQblMYmG +8liATQvpdhv7h4gDpfsGvmrcrP7POJpvNRskbYzirLKBMkiBNA6Nazxscx8LckbF +kAOXx125FuLZGI+ecMreD8uKtNq8fmXXAITcdCzYGjukM2yX29/oD52tFVNlVG0I +rvLfAjJrWj5CAy5AS1y0M9m+XyNoEiSwo5IwVzxJAEbUMuOwSfJCA1e2k0aehzWr +9FfUxBmBoqGPZDbRBmFbjvLZeEEP6Ku3PEWQDvaVlpcYh5bRLZ6eAkgg6aIoHX78 +PEjY9bBM5WqG2yWTE8dqapZEDpIateaKyeqJLw14dEMe6qKiTXqjLqvurHKhT4Sw +90HsYn8mpHtNza3t2NXOHS79wzlNykpTfs7DiQZM2r8jNBeHtxqImyBrdZ1EUomr +Y9tTINoOgTLuKEJ9V5WzP+OEkHGI2Tznav+IyASLjz/jc/nywpW+C4zwzWjDhz3N +89LaKrBkYTfoSRI4es2TUquMU046UNKY5R1Yk8BzH1I+q9eoWaabBqUpsMQwlKCO +be9eRVFzzIBdyavne7xzds4nu428kfTdC/MTGrTkM3qqY8puzTXKUWyl5ZjlDtzh +rkg4kpJruEdWJvU= +-----END CERTIFICATE----- diff --git a/tests/certs/ca.key.pem b/tests/certs/ca.key.pem new file mode 100644 index 0000000..22f0ac2 --- /dev/null +++ b/tests/certs/ca.key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +iyEPRUkeLSviCMOfCstOsyiWFBtA +-----END PRIVATE KEY----- diff --git a/tests/certs/cert.csr.pem b/tests/certs/cert.csr.pem new file mode 100644 index 0000000..da7de5b --- /dev/null +++ b/tests/certs/cert.csr.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIIEnjCCAoYCAQAwWTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9j +YWxob3N0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA03UFnp0RMLG2 +pLlTBxi3lPBRrQ+hYcHTYVXopUY4FVA06C5CK1meYKFFd4+Cu8doqi/1RJ3hQhA1 +Zjch4XqjPBv8G1gfxsh1hzhmE4kivVKEDJsXfhQN2cibgGcfNpphCYAMUq1tfi53 +4Vcv8dRSEr2moDC3vY63KeLbS1/91NLxuCyInvJgu1piIG2rbsoyOyps6+Wt7dGu +0B4XnKrApdyRZBzphW2fDpC1OqkSTHZl0Dr3XIDeEeI4MbrcbnfBHij+vStCbriB +7GeAiXl6od2EIDeEFNxKwjaVzYDAi2YwET2qXMiT5ihTverhYRnu+mWeukKbsCz6 +LS9k+cTsPXnfM+9F7LLTZQVVA6wO7TNgQMzYksjzfa+Sjn+sJgEHba5Ab9jWf37e +ToMyJpjH9wJ0FxncliWDyNB+X0BRgY6Yd1PgV/cIXL/XgxwYUv9LJhNhZSONYNET +Fkw+2n/smMEBp7R71sOCBY7fu6I5cSxRK6t2peAzSUFIdFb7a47MaDiK4+4Mnf8w +wIedq1ZjsUBq3/wOf6NRszyFLDV6WSsF4udIixsSvZp0ArfQaJp8xI7kBTfS4mmK +kNu6uwgmq7lW5QWxMh8lQFof1IXanH8ium1Olwo+tzB/hIkuVGKljVOmbpKKv1HO +qnNQFmHT4uynQeCqC3ArF4WdrWYD+k8CAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IC +AQDF8ISsk7WvgLbU/Gxzjmn1hEtw0lS3EnpNxNcPtIgVK6+hwsGw+mg+VCpUN9g6 +Rndjht2OSaTb4P/4Og3rsxSwwI8pfWzgpUx8PhzXYGUoHumsGwDfcT0gwU2OSbi1 +i3Q8pV9RDqsJpOY3cuPOvEuGyVgGSQrlbmLzTr/16FK1rvVjkYOTXxFGSrapzgag +nf5P38v9wcX8hOvXUlaF7rHRHMvDvwrJPxVPzeQeHqD8+6rFj6GDLvza2JQ/4rWN +1LutGVEmHb8hpGtCVDikZo5BpRrFTI18bBbpSLYKzGDA5e/6j7r8MdxHwAnxkdBG +0SkEfOy8wzmPs0ZOGu5nI0Pl/KXS2LFBGJEYoVlWfneJiNCW2/Yl9tAiKWOBn7pl +s2XKfT/w/5+Fad/BpPc/U2iPbSreDfkoW8JquJQJj9rUpKi7owXF56QqYtg5wTIS +iJN4vt24TDqf/3eurGTQpWZuZ/4L36S5lj67v5iM9WD48g8DlRqi3nthGl7r2NTO +VUz/SgHyNCGkIiIRNDQgrUznpQ1BH0EeaBLSy/QooaTqDM4H32WYqxE8/ZpP45fd +X47H8HX8zlJqIsDjI4vZm8ZabRR7MCqdHaHntQJVFfygUvNgz4zoxc6XtdopCORc +1qVu+KPIh2sutNIRL+3sywz4BP8/TNBCfI0JqZrmAFpwAA== +-----END CERTIFICATE REQUEST----- diff --git a/tests/certs/cert.pem b/tests/certs/cert.pem new file mode 100644 index 0000000..8d911ef --- /dev/null +++ b/tests/certs/cert.pem @@ -0,0 +1,30 @@ +-----BEGIN CERTIFICATE----- +MIIFKDCCAxACCQCk1Yn+rgIVRDANBgkqhkiG9w0BAQsFADBRMQswCQYDVQQGEwJH +QjETMBEGA1UECAwKU29tZS1TdGF0ZTEYMBYGA1UECgwPTXkgRmFrZSBDQSBMdGQu +MRMwEQYDVQQDDApmYWtlY2EubmV0MCAXDTE3MDUxMjE1MTUxN1oYDzQ3NTUwNDA5 +MTUxNTE3WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G +A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDDAlsb2NhbGhv +c3QwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDTdQWenREwsbakuVMH +GLeU8FGtD6FhwdNhVeilRjgVUDToLkIrWZ5goUV3j4K7x2iqL/VEneFCEDVmNyHh +eqM8G/wbWB/GyHWHOGYTiSK9UoQMmxd+FA3ZyJuAZx82mmEJgAxSrW1+LnfhVy/x +1FISvaagMLe9jrcp4ttLX/3U0vG4LIie8mC7WmIgbatuyjI7Kmzr5a3t0a7QHhec +qsCl3JFkHOmFbZ8OkLU6qRJMdmXQOvdcgN4R4jgxutxud8EeKP69K0JuuIHsZ4CJ +eXqh3YQgN4QU3ErCNpXNgMCLZjARPapcyJPmKFO96uFhGe76ZZ66QpuwLPotL2T5 +xOw9ed8z70XsstNlBVUDrA7tM2BAzNiSyPN9r5KOf6wmAQdtrkBv2NZ/ft5OgzIm +mMf3AnQXGdyWJYPI0H5fQFGBjph3U+BX9whcv9eDHBhS/0smE2FlI41g0RMWTD7a +f+yYwQGntHvWw4IFjt+7ojlxLFErq3al4DNJQUh0VvtrjsxoOIrj7gyd/zDAh52r +VmOxQGrf/A5/o1GzPIUsNXpZKwXi50iLGxK9mnQCt9BomnzEjuQFN9LiaYqQ27q7 +CCaruVblBbEyHyVAWh/UhdqcfyK6bU6XCj63MH+EiS5UYqWNU6Zukoq/Uc6qc1AW +YdPi7KdB4KoLcCsXhZ2tZgP6TwIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQCGgPUO +X9y8YCuF9ZS2f/Bz7nH2k4tj0A7XI0w7ZT6GIjJtzWtjqAounFpoRuRVeVKwHi26 +lPDimGAgcS3vQLdoWxI6Vz4DdHXnI197Gz9yih+gSC/YPRjxihgYzAjJf7ZdR3DV +0RGUHGW0qoOYun3owY+L33ekLeIvGxB4yQyihXVRxZMKKAV1jfjGx/VNL8FaTjk3 +6spu/nUg2ewYBhq1/UBnVolODC6l+Dhz7/1MLHsoUeC5D53qLazQtcCmz1RQLfs1 +I9ME7HyTdjACHHZy4OoNZ4f5jUqGfgcfYXVMh6C5b8uiaBJx/jleRCEB+XKhbwhK +QYS7z/Uxup/8MX4crV8i7WP73I/ITeRgXnamOGCXbVFJW5CrOBx4EMc9fXkVv1Vj +w/U05mVLVYyT28z2Cn7WDNWo6Tx0JbDUZkcm+v8XHGuqcsIF372EfF2Jv0RMFDHY +Zw5OHFlTIo67qeEf7A/rMpfFxJJ3/vk4AS1uToPLt0PC2aiJE6wq5P6/CqzvqG6N +xg3g7a258znW5D19FW0Tj0F0H2Qc1kOxmFrdK6+KFLkgqClznKKt8zOKEZljZ9u7 +uFWcOMsK2roAx8iHKV7dEm9/0mTO3TJGWdMmwzVEBiWyOi2MIbFiac6A7KxUGjl5 +qsbqOZZNAaYg5lberKXgnoE6kgCH1IPhqRVTrA== +-----END CERTIFICATE----- diff --git a/tests/certs/key.pem b/tests/certs/key.pem new file mode 100644 index 0000000..36c9711 --- /dev/null +++ b/tests/certs/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDTdQWenREwsbak +uVMHGLeU8FGtD6FhwdNhVeilRjgVUDToLkIrWZ5goUV3j4K7x2iqL/VEneFCEDVm +NyHheqM8G/wbWB/GyHWHOGYTiSK9UoQMmxd+FA3ZyJuAZx82mmEJgAxSrW1+Lnfh +Vy/x1FISvaagMLe9jrcp4ttLX/3U0vG4LIie8mC7WmIgbatuyjI7Kmzr5a3t0a7Q +HhecqsCl3JFkHOmFbZ8OkLU6qRJMdmXQOvdcgN4R4jgxutxud8EeKP69K0JuuIHs +Z4CJeXqh3YQgN4QU3ErCNpXNgMCLZjARPapcyJPmKFO96uFhGe76ZZ66QpuwLPot +L2T5xOw9ed8z70XsstNlBVUDrA7tM2BAzNiSyPN9r5KOf6wmAQdtrkBv2NZ/ft5O +gzImmMf3AnQXGdyWJYPI0H5fQFGBjph3U+BX9whcv9eDHBhS/0smE2FlI41g0RMW +TD7af+yYwQGntHvWw4IFjt+7ojlxLFErq3al4DNJQUh0VvtrjsxoOIrj7gyd/zDA +h52rVmOxQGrf/A5/o1GzPIUsNXpZKwXi50iLGxK9mnQCt9BomnzEjuQFN9LiaYqQ +27q7CCaruVblBbEyHyVAWh/UhdqcfyK6bU6XCj63MH+EiS5UYqWNU6Zukoq/Uc6q +c1AWYdPi7KdB4KoLcCsXhZ2tZgP6TwIDAQABAoICAG5VnT0KD7IsyHRjDuOhDgl0 +6+k1QqvTiww6202BujnExBZyLKyZ8n3RKIIeK8T3Y7GJnJzVFHh4JKTVF6SjbREs +OPu7m7kJV+naj8iZwbqvl59MPZHXvPJqLlltdWf0XbWrTsLFI2C3tm2qJKY2dyy4 +MH1dbkJGeWQtrudBixzFhMLo2dC1/6tCmfj5wH3qmxMsK/FawspYorKNQRzKLANf +d62ZOHAapufesfGNzrJ/BNkGK5Pk2frS4ydvrR0m92lZUrI6JEvJkmFcEEGrVUWo +ZAy7avvHhmqYOMol6FHUXDhiyMXpEuMBH9D1Aey8r2mQJKZjowDmNxPkqvd4vRrT +siGWi+S0sfovsLY7uGYNSGtAER45tnlDYKjiqLlXxd0S22Kgqz1LGdxRxrQvU1M1 +WYUKyrR6WzehrJJELLQAhTNoqHWxgugIjSodR2kQQlGKD/EPQbjq+LPiJW9clEaq +x43q/TNY6c1P7ZXkcqaNF4fdojKoQRuHPd/6wfJ5nOx91y/uqHHHC+8izdE4x0g/ +3hZFslioOo6qMtVMA0SCTn/BsIspPqSQG9kDqcenZjM7o4kqEmW/ocF1bHkjvEza +yxic+Zz0ASyUnfupk7YixqQ3aeHm7S8hWcKqE9ep8LCSdt4JDDhgtHY/2f+EbLDe +KZjjNwP4hss0SFbrh6MhAoIBAQDzgw8HfqnF87ytk6yhawsbOjMgDoMkPgjHFkz8 +NdwZk4JsY+rFiH1XOl1U5knTAytj+6Cs/NHbM4cyS7a4qPWd4Uo86YmkmJoWhbQo +zlVj37RbnKZQAaWrZk2i9wPtt3I7SB6vA45SFFVrZ+kezKgO+DtXd6Dm4JsbbOG6 +bTUIk4d5t2V11lZLlA4IS/Vq74Yx1D4rRqD82TEPvqS/456lFJAuTfKHAtdbTVXD +Yx8JFXC8FG3q4xWVJpzi50uWrk/EWR0P2e3XTP1F+B/XCyHYLYMewgM0DGGAPq2B ++yxPkJ4fgRPikbbyTI2r9Tjh7q6fhXwM7iSn0JcnKw2EAHrzAoIBAQDeTSHQ46en +643b4YQ5saywlIvrZs5fOys6wojwAC99J2cxv19IzXCTFufH5ESTpPmMtSxWxsxO +v2W5mJd0pqil/mLMEpPLcJ4DQ/QvBJcq677Dd0jCVih4o/VoYUANZ3a44lhXd0b9 +abBOJeENKwD+dWEfTrNx+SVyPKkJHyror5gbqHrNbsh4EY5sTAVYLi3Re6phpm73 +TFkOAsCaYxHNGXbGZFr/fci68NN3vq7c+JIvaP5Mo/sMsNf6wgSgtm5KRm6iP2as +5K9RC0ku+td2j5cV6EoLzhfklyvdFA+BmzBmfNmQqdFEboGZ7S5sP/U+47vZbJz4 +uVbfPqVdIOI1AoIBAQCmlGETn3JEGBhaJUtSoQdX+NhAda0Afe9DQd4oKjAGzwHJ +ZBCWUGht4181VNPrpZ9pJhkkPU7RxM4XefPlHq/m8Nb4FEo0bq/5+DzmLT/8smuO +v/qFgJr0RWeWs6PcWkGqjAtxbd1OCNrmPo8aoqXUl7sAerV3Ou7gM/u2rD47P7DN +csfVdGpUCVQQGOYp4PDL5YUh1Y8LaQhG3QOWwP8lrotzIZ9m/GqV6rFgl84DSMd1 +4gPPSsjY3zEzh0YOLfAjQVLAJTSus9wOoU4mrVbZq0inIdFPKakJMdc/s7L8OtHw +ezw/Ia8k0XmYyIfBBqYuOgJCViZ9JhRAlxMH2cxBAoIBAEVHNGqtB+rE9gi4V0TD +olXFdXV/LIFxJx/A0Hw7EpGmXfqa8U7290ZftQwOj3CT0VGQhKjme3VvIHyemVS5 +O4W/qXp/r5+PES9jVg3xGEs//MtduUcXIrW2IxzMcfG7dLxZjb50ZaND9LXmCCTa +agzi98mR9UpnosvfK660D+NEFdtIvjyXcdI5ZnFJn8DDY3QNXiPggmR/XqGHKg7z +IPESJnndfJ5yzMO1mbGPs18Z+DTeO6IW2H4z/psfK+IJ+IUdPg1CRPVMr3ZKVhxW +AB/qz+L76VeUes26bukO/2YcvxCxh/wVcCorZihaSkViCUP4JOq1dpXs7K3WECLd +xKECggEASP/nP1G+YSLLpFpz0jpFEMY9cy7/qFt89nLud9qavXXDYtzggaHEehkq +wR6j9ySFavDPiD/WQ9fh0dRSQiorqgJfKfZipi3LTD2WTrzEnRYbMu84mboEzeUc +4s3R1K5FiTP3RV4FQO4mjtYd9LeiIQvez6pf20GSGKCQ4na3xwX79l4dRnq6Wfti +miLvb+cfK1JS8KdSsR6m16rLmlCqFt5DFk6+fZZeIimQReB5rA7A58chKT/sXOuv +wIEqv82zqU18/dUK9BO+5Yy/2hhcwzUVv0ya8yBOzwcVE8Xwb4f2DeolOhEwX1Yj +Vwd26kNim/KnSnKTPPpXGeba05Fobw== +-----END PRIVATE KEY----- diff --git a/tests/certs/wrongca.cert.pem b/tests/certs/wrongca.cert.pem new file mode 100644 index 0000000..b2e3e10 --- /dev/null +++ b/tests/certs/wrongca.cert.pem @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- +MIIFezCCA2OgAwIBAgIJAIO1lh/OO09sMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMRYwFAYDVQQKDA1Xcm9uZyBGYWtl +IENBMRcwFQYDVQQDDA53cm9uZy5mYWtlLm5ldDAgFw0xNzA1MTUxMTA5NDRaGA80 +NzU1MDQxMjExMDk0NFowUzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3Rh +dGUxFjAUBgNVBAoMDVdyb25nIEZha2UgQ0ExFzAVBgNVBAMMDndyb25nLmZha2Uu +bmV0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAwEKGzxq4YAcVl4uT +KFWVuvMpmE5b6RLVn9D1EJH24NKSFD7ZuBbkfBtMjMequNOOKSbFH9hhgrC6zGHs +91gnO0lTxLRM8Y6TyLiFPckNSIlLkKTCKXUEwjeQVzDFSOdtQtjRm2ZwYQ+89RN0 +jw9KVcapqohEVmxmz5zDh43kWr1wKBLErUKknKZ61cylO+4Ii+YcBh6vVs2W2Aln +NgR5aov+lM59ty9ZuMGYUKtPUGQAMwJMhKlEW5ndDTiJ1epfVStZdk0Rnk74oTj2 ++qP4B/0ogbO2qySUKVuvBFDmMqk7SfcOJ6TLT1NwdB12f1A5PoQTccXHo2WPZj9/ +ax3tJ9wf4bPkfNszJO6KIdqLk8mNoO9EaKdK5Er9rBnDTfbvqjGT2S9xsfMjt64l +x6eEUCMmfVy9RAY0er0ZoRVYGIbfsJ1SSVWVGDMGJnPxmaXWevtTVXqRiLlKOo+x +MKqlVTqk8U7oOs8JWz3yfoyKbExOGMOYug5jTyOMGyoji8Y1AlikPpEkmgzCeH7y +67rmhhAxHq02yQeKkczyLANNk+pfmNdGvrJuy43jtq2GqcxVEReB1HjPMCMsMevJ +0Ubw2FtcLuRuMLatUklSX5rGJU/tvBTWUm9gFIt693nj/SyDn/oJ9eRTfRH7kqjP +3ejDRNkEFdN8xud8WzH6GqFs8GcCAwEAAaNQME4wHQYDVR0OBBYEFDc0z/870B0l +cvjA/X7MKDvGXW7QMB8GA1UdIwQYMBaAFDc0z/870B0lcvjA/X7MKDvGXW7QMAwG +A1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggIBAC2yY1JncXCyj43NX4z3ySmp +nZccPBC1z8H/oITz1H2iMay+c/QZqK70qaULswTHxQtWz+kjm/B91IcX4wYM49yE +n+bUoDMoDCoVtttkGylWmUDqIw/nf4SMIzatajrc6SgxhaFIo2d+aJwiesiQgpEY +nCJzRyM25lz2lX5ilORPc9gmB/9udIfvAVg2OAtO0tVQBUjPZ2L+04iAGZbDi5cJ +6c4SIpR2P6TeL+1qaMSwjmfyRdX/QKH9cpPAusKMG3S59sYALJbX8++kBTbwFoao +r0MZUVim5IlbLCd296GOTzQqJzpZlRc4kIxpRj6ChneCanfAD391xBQhTSq06e4c +5yQO7Nvv7yQUkIM2ys3KwzDhv878x/TbO3wV3hDgr3xCoT21yS6SgnKUK+5UDSgI +6qRXQ5e3/lyQYyRNJ2VWYSJWQpibPPRHmYPNobsDS1NWyJeHiRpdwc7v3dxqhv+t +LpTBTFJnwgMjZ2xVzJlzxJ26D9Cw/ttxjtu7uEp9ZkPiIn9AojRHKTHBDbKlFzOc +LUGcLLUz1JXn/KWjfbzkj4XCcrr0aaoFpBNu+WxYTWHY/GhXFtKCUXO2xS3y90Yz +6N2LlE5Ys6v68WySPC3vBZbZgrYPduX70OZ42Z0F5Zth7z0lebrg/WerkFobzsm6 +dSgE5JbOqYUKUHWeFKjS +-----END CERTIFICATE----- diff --git a/tests/test_http.lua b/tests/test_http.lua index 73cd9f3..4216c6c 100644 --- a/tests/test_http.lua +++ b/tests/test_http.lua @@ -31,7 +31,7 @@ function setup() end function teardown() - -- socket has already been closed in http_responde + -- socket has already been closed in http_respond --server.sock:close() end @@ -68,7 +68,7 @@ function http_read(sock) return res end -function http_responde(sock) +function http_respond(sock) sock:send("HTTP/1.1 200 OK\r\nServer: nginx/1.2.6\r\nDate: Mon, 10 Mar 2014 22:25:51 GMT\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Language: en-us\r\nExpires: Mon, 10 Mar 2014 22:25:51 GMT\r\nVary: Accept-Language, Cookie\r\nLast-Modified: Mon, 10 Mar 2014 22:25:51 GMT\r\nCache-Control: max-age=0\r\n\r\n{\"id\": \"02c7830aae684d0088a0616a9ed81a6b\"}") sock:close() end @@ -88,7 +88,7 @@ function test_capture_message() local json_str = http_read(client) --local json_str = get_body(res) local json = cjson.decode(json_str) - http_responde(client) + http_respond(client) assert_not_nil(json) assert_equal("undefined", json.server_name) @@ -119,7 +119,7 @@ function test_capture_exception() local json_str = http_read(client) --local json_str = get_body(res) local json = cjson.decode(json_str) - http_responde(client) + http_respond(client) assert_not_nil(json) assert_equal("undefined", json.server_name) diff --git a/tests/test_https.lua b/tests/test_https.lua new file mode 100644 index 0000000..861d6e9 --- /dev/null +++ b/tests/test_https.lua @@ -0,0 +1,155 @@ +require "lunit" +local socket = require "socket" +local raven = require "raven" +local cjson = require "cjson" +local posix = require "posix" +local ssl = require "ssl" + +local print = print +local error = error +local string_find = string.find +local string_sub = string.sub +local string_match = string.match +local os_exit = os.exit +local random = math.random + +-- generate HTTPS certs: +-- create CA: openssl req -x509 -newkey rsa:4096 -keyout ca.key.pem -out ca.cert.pem -days 1000000 -nodes +-- create CSR: openssl req -newkey rsa:4096 -keyout key.pem -out cert.csr.pem -nodes +-- sign CSR: openssl x509 -req -in cert.csr.pem -CA ca.cert.pem -CAkey ca.key.pem -CAcreateserial -out cert.pem -days 1000000 + +math.randomseed(os.time()) + +module("test_https", lunit.testcase) + +local server = {} +local rvn +local dsn +local port = -1 + +local server_sslparams = { + mode = "server", + protocol = "tlsv1_2", + key = "./tests/certs/key.pem", + certificate = "./tests/certs/cert.pem", + verify = "none", + options = "all" +} + + +function setup() + port = random(20000, 65535) + --port = 10000 + local sock = socket.tcp() + assert(sock) + assert(sock:bind("*", port)) + assert(sock:listen(64)) + server.sock = sock +end + + +function teardown() + -- socket has already been closed in http_respond + --server.sock:close() +end + +local function get_dsn() + dsn = "https://pub:secret@127.0.0.1:" .. port .. "/sentry/proj-id" + return dsn +end + + +function get_body(response) + local i = assert(string_find(response, "\n\n")) + return string_sub(response, i + 1) +end + +function http_read(sock) + local content_len + function get_data() + return function() return sock:receive("*l") end + end + for res, err in get_data() do + if res == "" then + break + end + local s1, s2, len = string_find(res, "Content%-Length: (%d+)") + if s1 and s2 then + content_len = len + end + end + local res, err = sock:receive(content_len) + + if not res then + error("receive failed: " .. err) + end + return res +end + +function http_respond(sock) + sock:send("HTTP/1.1 200 OK\r\nServer: nginx/1.2.6\r\nDate: Mon, 10 Mar 2014 22:25:51 GMT\r\nContent-Type: application/json\r\nConnection: close\r\nContent-Language: en-us\r\nExpires: Mon, 10 Mar 2014 22:25:51 GMT\r\nVary: Accept-Language, Cookie\r\nLast-Modified: Mon, 10 Mar 2014 22:25:51 GMT\r\nCache-Control: max-age=0\r\n\r\n{\"id\": \"02c7830aae684d0088a0616a9ed81a6b\"}") + sock:close() +end + +function test_validate_cert_ok() + local cpid = posix.fork() + if cpid == 0 then + local client = server.sock:accept() + client = ssl.wrap(client, server_sslparams) + assert(client:dohandshake()) + local json_str = http_read(client) + local json = cjson.decode(json_str) + http_respond(client) + os_exit() + else + rvn = raven:new(get_dsn(), { + tags = { foo = "bar" }, + cafile = "./tests/certs/ca.cert.pem", + }) + local id = rvn:captureMessage("Sentry is a realtime event logging and aggregation platform.") + assert_not_nil(id) + assert_not_nil(string_match(id, "%x+")) + end +end + +function test_validate_cert_failure() + local cpid = posix.fork() + if cpid == 0 then + local client = server.sock:accept() + client = ssl.wrap(client, server_sslparams) + local ok, err = client:dohandshake() + assert(not ok) + os_exit() + else + -- load the certificate for another CA, it must fail + rvn = raven:new(get_dsn(), { + tags = { foo = "bar" }, + cafile = "./tests/certs/wrongca.cert.pem", + }) + local ok, err = rvn:captureMessage("Sentry is a realtime event logging and aggregation platform.") + assert_nil(ok) + assert_equal('certificate verify failed', err) + end +end + +function test_no_validate_cert() + local cpid = posix.fork() + if cpid == 0 then + local client = server.sock:accept() + client = ssl.wrap(client, server_sslparams) + assert(client:dohandshake()) + local json_str = http_read(client) + local json = cjson.decode(json_str) + http_respond(client) + os_exit() + else + rvn = raven:new(get_dsn(), { + tags = { foo = "bar" }, + verify_ssl = false, + }) + local id = rvn:captureMessage("Sentry is a realtime event logging and aggregation platform.") + assert_not_nil(id) + assert_not_nil(string_match(id, "%x+")) + end +end + diff --git a/tests/test_sanity.lua b/tests/test_sanity.lua index d66ee58..cfcadc6 100644 --- a/tests/test_sanity.lua +++ b/tests/test_sanity.lua @@ -14,7 +14,7 @@ function test_parse_dsn() assert_equal("public", obj.public_key) assert_equal("secret", obj.secret_key) assert_equal("example.com", obj.host) - assert_equal(80, obj.port) + assert_equal(nil, obj.port) assert_equal("/sentry/", obj.path) assert_equal("project-id", obj.project_id) assert_equal("/sentry/api/project-id/store/", obj.request_uri) @@ -64,7 +64,7 @@ end function test_parse_host_port1() local host, port = raven._parse_host_port("http", "somehost.com") assert_equal("somehost.com", host) - assert_equal(80, port) + assert_equal(nil, port) end function test_parse_host_port2()