Major fix for a race condition vulnerability
This commit is contained in:
parent
e251d2def6
commit
d132b6a2a1
|
@ -12,7 +12,7 @@ app = Landline::Server.new do
|
|||
end
|
||||
path "important" do
|
||||
preprocess do |req|
|
||||
|
||||
puts "This is a context for #{request}" if defined? request
|
||||
# Implement logging logic here
|
||||
puts "Client at #{req.headers['remote-addr']} wanted to access something /important!"
|
||||
end
|
||||
|
|
|
@ -8,23 +8,21 @@ Landline::Session.hmac_secret = "Your secure signing secret here"
|
|||
app = Landline::Server.new do
|
||||
get "/make_cookie" do
|
||||
session["random_number"] = Random.random_number(100)
|
||||
text = <<~HTML
|
||||
<<~HTML
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>
|
||||
Session test #{request.to_s}
|
||||
Session test
|
||||
</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Your random number is #RAND#!</h1>
|
||||
<h1>Your random number has been created!</h1>
|
||||
<hr>
|
||||
<p>Go check it at <a href="/check_cookie">this link!</a></p>
|
||||
</body>
|
||||
</html>
|
||||
HTML
|
||||
sleep(20)
|
||||
text.gsub("#RAND#", request.to_s)
|
||||
end
|
||||
|
||||
get "/check_cookie" do
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../../lib
|
|
@ -22,6 +22,10 @@ module Landline
|
|||
@hmac_secret ||= ENV.fetch('HMAC_SECRET', SecureRandom.base64(80))
|
||||
end
|
||||
|
||||
# Class for representing session errors
|
||||
class SessionError < ::StandardError
|
||||
end
|
||||
|
||||
# Class for representing session storage
|
||||
class Session
|
||||
def initialize(cookie, cookies_callback)
|
||||
|
@ -40,7 +44,7 @@ module Landline
|
|||
# Retrieve data from session storage
|
||||
# @param key [String, Symbol] serializable key
|
||||
def [](key)
|
||||
raise StandardError, "session not valid" unless @valid
|
||||
raise Landline::Session::SessionError, "session not valid" unless @valid
|
||||
|
||||
unless key.is_a? String or key.is_a? Symbol
|
||||
raise StandardError, "key not serializable"
|
||||
|
@ -53,7 +57,7 @@ module Landline
|
|||
# @param key [String, Symbol] serializable key
|
||||
# @param value [Object] serializable data
|
||||
def []=(key, value)
|
||||
raise StandardError, "session not valid" unless @valid
|
||||
raise Landline::Session::SessionError, "session not valid" unless @valid
|
||||
|
||||
unless key.is_a? String or key.is_a? Symbol
|
||||
raise StandardError, "key not serializable"
|
||||
|
|
|
@ -31,8 +31,20 @@ module Landline
|
|||
end
|
||||
end
|
||||
|
||||
# Ephemeral proxy class to which callback execution binds
|
||||
class PathExecutionOrigin
|
||||
def initialize(request, properties)
|
||||
@request = request
|
||||
@properties = Landline::Util::LookupROProxy.new(properties)
|
||||
end
|
||||
|
||||
attr_accessor :response
|
||||
attr_reader :request, :properties
|
||||
end
|
||||
|
||||
# Primary building block of request navigation.
|
||||
class Path < Landline::Node
|
||||
ExecutionOrigin = Landline::PathExecutionOrigin
|
||||
ProcContext = Landline::ProcessorContext
|
||||
Context = Landline::PathContext
|
||||
|
||||
|
@ -50,8 +62,6 @@ module Landline
|
|||
# Contexts setup
|
||||
context = self.class::Context.new(self)
|
||||
context.instance_exec(&setup)
|
||||
# TODO: This isn't fine
|
||||
@proccontext = self.class::ProcContext.new(self)
|
||||
end
|
||||
|
||||
# Method callback on successful request navigation.
|
||||
|
@ -96,12 +106,19 @@ module Landline
|
|||
|
||||
private
|
||||
|
||||
# Create an execution context for in-path processing blocks
|
||||
def get_context(request)
|
||||
exec_origin = self.class::ExecutionOrigin.new(request, @properties)
|
||||
self.class::ProcContext.new(exec_origin)
|
||||
end
|
||||
|
||||
# Sequentially run through all filters and drop request if one is false
|
||||
# @param request [Landline::Request]
|
||||
# @return [Boolean] true if request passed all filters
|
||||
def run_filters(request)
|
||||
proccontext = get_context(request)
|
||||
@filters.each do |filter|
|
||||
return false unless @proccontext.instance_exec(request, &filter)
|
||||
return false unless proccontext.instance_exec(request, &filter)
|
||||
end
|
||||
true
|
||||
end
|
||||
|
@ -109,8 +126,9 @@ module Landline
|
|||
# Sequentially run all preprocessors on a request
|
||||
# @param request [Landline::Request]
|
||||
def run_preprocessors(request)
|
||||
proccontext = get_context(request)
|
||||
@preprocessors.each do |preproc|
|
||||
@proccontext.instance_exec(request, &preproc)
|
||||
proccontext.instance_exec(request, &preproc)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -126,7 +144,6 @@ module Landline
|
|||
# @return [Boolean] true if further navigation will be done
|
||||
# @raise [UncaughtThrowError] by default throws :response if no matches found.
|
||||
def process_wrapped(request)
|
||||
@request = request
|
||||
return false unless run_filters(request)
|
||||
|
||||
run_preprocessors(request)
|
||||
|
@ -149,7 +166,7 @@ module Landline
|
|||
# @param request [Landline::Request]
|
||||
def exit_stack(request, response = nil)
|
||||
request.run_postprocessors(response)
|
||||
false
|
||||
response
|
||||
end
|
||||
|
||||
# Try to perform indexing on the path if possible
|
||||
|
@ -174,8 +191,9 @@ module Landline
|
|||
# @param backtrace [Array(String), nil]
|
||||
# @raise [UncaughtThrowError] throws :finish to stop processing
|
||||
def _die(errorcode, backtrace: nil)
|
||||
proccontext = get_context(request)
|
||||
throw :finish, [errorcode].append(
|
||||
*@proccontext.instance_exec(
|
||||
*proccontext.instance_exec(
|
||||
errorcode,
|
||||
backtrace: backtrace,
|
||||
&(@properties["handle.#{errorcode}"] or
|
||||
|
|
|
@ -35,6 +35,17 @@ module Landline
|
|||
end
|
||||
end
|
||||
|
||||
# Ephemeral proxy class to which callback execution binds
|
||||
class ProbeExecutionOrigin
|
||||
def initialize(request, properties)
|
||||
@request = request
|
||||
@properties = Landline::Util::LookupROProxy.new(properties)
|
||||
end
|
||||
|
||||
attr_accessor :response
|
||||
attr_reader :request, :properties
|
||||
end
|
||||
|
||||
# Test probe. Also base for all "reactive" nodes.
|
||||
class Probe < Landline::Node
|
||||
# @param path [Object]
|
||||
|
|
|
@ -12,8 +12,6 @@ module Landline
|
|||
def initialize(path, **args, &exec)
|
||||
super(path, **args)
|
||||
@callback = exec
|
||||
@context = Landline::ProbeContext.new(self)
|
||||
@response = nil
|
||||
end
|
||||
|
||||
attr_accessor :response
|
||||
|
@ -34,20 +32,21 @@ module Landline
|
|||
# @return [Boolean] true if further navigation is possible
|
||||
# @raise [UncaughtThrowError] may raise if die() is called.
|
||||
def process(request)
|
||||
@response = nil
|
||||
origin = Landline::ProbeExecutionOrigin.new(request, @properties)
|
||||
context = Landline::ProbeContext.new(origin)
|
||||
return reject(request) unless request.path.match?(/^\/?$/)
|
||||
|
||||
@request = request
|
||||
response = catch(:break) do
|
||||
@context.instance_exec(*request.splat,
|
||||
**request.param,
|
||||
&@callback)
|
||||
context.instance_exec(*request.splat,
|
||||
**request.param,
|
||||
&@callback)
|
||||
end
|
||||
return false unless response
|
||||
|
||||
if @response and [String, File, IO].include? response.class
|
||||
@response.body = response
|
||||
throw :finish, @response
|
||||
oresponse = origin.response
|
||||
if oresponse and [String, File, IO].include? response.class
|
||||
oresponse.body = response
|
||||
throw :finish, oresponse
|
||||
end
|
||||
throw :finish, response
|
||||
end
|
||||
|
|
|
@ -33,5 +33,19 @@ module Landline
|
|||
|
||||
attr_accessor :parent
|
||||
end
|
||||
|
||||
# Read-only lookup proxy
|
||||
class LookupROProxy
|
||||
def initialize(lookup)
|
||||
@lookup = lookup
|
||||
end
|
||||
|
||||
# Get a value by key
|
||||
# @param key [#hash] key for value
|
||||
# @return [Object,nil]
|
||||
def [](key)
|
||||
@lookup.[](key)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue