Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .bright-harness-build.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
$ docker build -t bright-harness-local -f Dockerfile.harness .

#0 building with "desktop-linux" instance using docker driver

#1 [internal] load build definition from Dockerfile.harness
#1 transferring dockerfile: 1.03kB done
#1 DONE 0.0s

#2 [internal] load metadata for docker.io/library/ruby:2.3
#2 DONE 0.3s

#3 [internal] load .dockerignore
#3 transferring context: 96B done
#3 DONE 0.0s

#4 [1/6] FROM docker.io/library/ruby:2.3@sha256:78cc821d95c48621e577b6b0d44c9d509f0f2a4e089b9fd0ca2ae86f274773a8
#4 resolve docker.io/library/ruby:2.3@sha256:78cc821d95c48621e577b6b0d44c9d509f0f2a4e089b9fd0ca2ae86f274773a8 done
#4 DONE 0.0s

#5 [internal] load build context
#5 transferring context: 8.15kB done
#5 DONE 0.0s

#6 [3/6] RUN sed -i 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' /etc/apt/sources.list && sed -i 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' /etc/apt/sources.list && sed -i '/stretch-updates/d' /etc/apt/sources.list && printf 'Acquire::Check-Valid-Until "false";\nAcquire::AllowInsecureRepositories "true";\n' > /etc/apt/apt.conf.d/99archive && apt-get update -o Acquire::Check-Valid-Until=false && apt-get install -y --allow-unauthenticated --no-install-recommends build-essential libxml2-dev libxslt1-dev libpq-dev pkg-config zlib1g-dev && rm -rf /var/lib/apt/lists/*
#6 CACHED

#7 [2/6] WORKDIR /app
#7 CACHED

#8 [4/6] COPY Gemfile Gemfile.lock ./
#8 CACHED

#9 [5/6] RUN gem install bundler -v 1.10.6 && bundle _1.10.6_ config build.nokogiri --use-system-libraries && bundle _1.10.6_ install --without development test --jobs 4 --retry 3
#9 CACHED

#10 [6/6] COPY . /app
#10 DONE 0.0s

#11 exporting to image
#11 exporting layers 0.0s done
#11 exporting manifest sha256:8fdd97da88ce8307f57ee4ca8ccbc4770b637c54ee366194c3e559fb8b5fa637 done
#11 exporting config sha256:24316070372631afc6997f61e47778d916e284e0669de97ef0c0ded778772c1c done
#11 exporting attestation manifest sha256:7aeaa672b3666afefd186aac194efd8313b4f0711c4d0bb2af8a80e692eee499 done
#11 exporting manifest list sha256:086a8c082ba7472f334f0abbd4bf075223c1b5adbcff4093ab89c8f6c9ecc255 done
#11 naming to docker.io/library/bright-harness-local:latest done
#11 unpacking to docker.io/library/bright-harness-local:latest 0.0s done
#11 DONE 0.1s

View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/mar8dauicn47jcsj1oc3i0p0l

[exit code=0 signal=null]
5 changes: 5 additions & 0 deletions .bright-harness-container.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
bright_harness.rb:193: syntax error, unexpected keyword_rescue, expecting keyword_end
rescue StandardError => e
^

[exit code=1 signal=null]
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Added by bright-agent to avoid permission errors
log/
29 changes: 29 additions & 0 deletions Dockerfile.harness
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM ruby:2.3

WORKDIR /app

RUN sed -i 's|http://deb.debian.org/debian|http://archive.debian.org/debian|g' /etc/apt/sources.list \
&& sed -i 's|http://security.debian.org/debian-security|http://archive.debian.org/debian-security|g' /etc/apt/sources.list \
&& sed -i '/stretch-updates/d' /etc/apt/sources.list \
&& printf 'Acquire::Check-Valid-Until "false";\nAcquire::AllowInsecureRepositories "true";\n' > /etc/apt/apt.conf.d/99archive \
&& apt-get update -o Acquire::Check-Valid-Until=false \
&& apt-get install -y --allow-unauthenticated --no-install-recommends \
build-essential \
libxml2-dev \
libxslt1-dev \
libpq-dev \
pkg-config \
zlib1g-dev \
&& rm -rf /var/lib/apt/lists/*

COPY Gemfile Gemfile.lock ./

RUN gem install bundler -v 1.10.6 \
&& bundle _1.10.6_ config build.nokogiri --use-system-libraries \
&& bundle _1.10.6_ install --without development test --jobs 4 --retry 3

COPY . /app

EXPOSE 3001

CMD ["ruby", "bright_harness.rb"]
197 changes: 197 additions & 0 deletions bright_harness.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'json'
require 'ostruct'
require 'rack'

ROOT = File.expand_path(__dir__)
SEARCH_EXCLUDES = %w[vendor node_modules tmp log .git coverage pkg .bundle].freeze

def text(status, body)
[status, { 'Content-Type' => 'text/plain' }, [body]]
end

def parse_body_hash(req)
raw = req.body.read.to_s
req.body.rewind if req.body.respond_to?(:rewind)
return {} if raw.strip.empty?

JSON.parse(raw)
rescue JSON::ParserError
req.POST
end

def symbolize_hash(value)
case value
when Hash
value.each_with_object({}) do |(k, v), out|
out[k.is_a?(String) ? k.to_sym : k] = symbolize_hash(v)
end
when Array
value.map { |item| symbolize_hash(item) }
else
value
end
end

def discover_project_file(rel_path)
direct = File.join(ROOT, rel_path)
return direct if File.file?(direct)

basename = File.basename(rel_path)
Dir.glob(File.join(ROOT, '**', basename)).find do |path|
next false unless File.file?(path)

relative = path.sub(%r{^#{Regexp.escape(ROOT)}/?}, '')
parts = relative.split(File::SEPARATOR)
next false if parts.any? { |part| SEARCH_EXCLUDES.include?(part) }

relative.end_with?(rel_path)
end
end

def safe_require_project_file(rel_path)
path = discover_project_file(rel_path)
raise LoadError, "Could not locate #{rel_path}" unless path

require path
path
end

def ensure_action_controller_parameters
return if defined?(ActionController::Parameters)

require 'action_controller'
end

def ensure_application_controller
return if defined?(ApplicationController)

ensure_action_controller_parameters unless defined?(ActionController::Base)
safe_require_project_file('app/controllers/application_controller.rb')
end

class MinimalHarnessController < ApplicationController
attr_accessor :session
attr_reader :harness_redirect

def initialize
@session = {}
end

def redirect_to(target, options = {})
@harness_redirect = [target, options]
end
end

def build_user_like_object(input)
data = symbolize_hash(input || {})
persisted_flag = if data.key?(:persisted?)
data[:persisted?]
elsif data.key?(:persisted)
data[:persisted]
else
nil
end

attrs = data.each_with_object({}) do |(key, value), out|
next if key == :persisted?

out[key] = value
end

user = OpenStruct.new(attrs)
user.define_singleton_method(:persisted?) { !!persisted_flag }
user
end

TARGETS = {}

begin
ensure_action_controller_parameters
safe_require_project_file('app/controllers/users_controller.rb')
if defined?(ActionController::Parameters) && ActionController::Parameters.instance_methods.include?(:permit)
TARGETS[['POST', '/harness/actioncontroller--parameters-permit']] = lambda do |req|
payload = parse_body_hash(req)
filters = payload['filters'] || payload[:filters] || req.params['filters'] || []
filters = filters.split(',') if filters.is_a?(String)
filters = Array(filters).map { |item| item.to_s.strip.sub(/^:/, '').to_sym }
user_input = payload['user'] || payload[:user] || {
email: 'a@example.com',
password: 'secret',
password_digest: 'attacker-controlled',
admin: true
}
params = ActionController::Parameters.new(user: user_input)
permitted = params.require(:user).permit(*filters)
text(200, "permit executed\nfilters=#{filters.inspect}\npermitted=#{permitted.to_h.inspect}")
end
end
rescue StandardError, LoadError => e
warn("Skipping ActionController::Parameters.permit harness: #{e.class}: #{e.message}")
end

begin
ensure_application_controller
if defined?(ApplicationController) && ApplicationController.instance_methods.include?(:login_user)
TARGETS[['POST', '/harness/applicationcontroller-login-user']] = lambda do |req|
payload = parse_body_hash(req)
user = build_user_like_object(payload['user'] || payload[:user] || {
id: 1,
password: 'secret',
persisted: true
})
controller = MinimalHarnessController.new
controller.login_user(user)
text(200, "login_user executed\nsession=#{controller.session.inspect}")
end
end
rescue StandardError, LoadError => e
warn("Skipping ApplicationController.login_user harness: #{e.class}: #{e.message}")
end

begin
ensure_application_controller
if defined?(ApplicationController) && ApplicationController.instance_methods.include?(:authenticate)
TARGETS[['POST', '/harness/applicationcontroller-authenticate']] = lambda do |req|
payload = parse_body_hash(req)
user = build_user_like_object(payload['user'] || payload[:user] || { password: 'secret' })
password = payload['password'] || payload[:password] || req.params['password'] || 'secret'
controller = MinimalHarnessController.new
result = controller.authenticate(user, password)
text(200, "authenticate executed\nresult=#{result.inspect}")
end
end
rescue StandardError, LoadError => e
warn("Skipping ApplicationController.authenticate harness: #{e.class}: #{e.message}")
end

begin
ensure_application_controller
if defined?(ApplicationController) && ApplicationController.instance_methods.include?(:prevent_login_signup)
TARGETS[['GET', '/harness/applicationcontroller-prevent-login-signup']] = lambda do |req|
session_user_id = req.params['session[:user_id]'] || req.params['user_id']
controller = MinimalHarnessController.new
controller.session[:user_id] = session_user_id unless session_user_id.nil? || session_user_id == ''
controller.prevent_login_signup
text(200, "prevent_login_signup executed\nredirect=#{controller.harness_redirect.inspect}\nsession=#{controller.session.inspect}")
end
end
rescue StandardError, LoadError => e
warn("Skipping ApplicationController.prevent_login_signup harness: #{e.class}: #{e.message}")
end

app = lambda do |env|
req = Rack::Request.new(env)
return text(200, 'ok') if req.get? && req.path == '/health'

handler = TARGETS[[req.request_method, req.path]]
return text(404, 'not found') unless handler

handler.call(req)
rescue StandardError => e
text(500, "#{e.class}: #{e.message}\n#{Array(e.backtrace).first(5).join("\n")}")
end

Rack::Handler::WEBrick.run(app, Host: '0.0.0.0', Port: (ENV['PORT'] || '3001').to_i)
Loading
Loading