thuban 0.4.0 → 0.4.1
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.
- package/dist/packages/scanner/executive-report.js +1 -1
- package/dist/packages/scanner/hallucination-detector.js +1 -1
- package/dist/packages/scanner/secret-scanner.js +1 -1
- package/package.json +4 -3
- package/dist/packages/crucible/seeds/python/seed-021 3.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-022 3.py +0 -34
- package/dist/packages/crucible/seeds/python/seed-023 3.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-024 3.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-025 3.py +0 -40
- package/dist/packages/crucible/seeds/python/seed-026 3.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-027 3.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-028 3.py +0 -42
- package/dist/packages/crucible/seeds/python/seed-030 3.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-031 3.py +0 -34
- package/dist/packages/crucible/seeds/python/seed-036 3.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-037 3.py +0 -41
- package/dist/packages/crucible/seeds/python/seed-038 3.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-039 3.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-040 3.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-041 3.py +0 -37
- package/dist/packages/crucible/seeds/python/seed-042 3.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-043 3.py +0 -32
- package/dist/packages/crucible/seeds/python/seed-044 3.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-045 3.py +0 -36
- package/dist/packages/crucible/seeds/python/seed-046 3.py +0 -33
- package/dist/packages/crucible/seeds/python/seed-047 3.py +0 -44
- package/dist/packages/crucible/seeds/python/seed-048 3.py +0 -35
- package/dist/packages/crucible/seeds/python/seed-049 3.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-050 3.py +0 -39
- package/dist/packages/crucible/seeds/python/seed-051 3.py +0 -38
- package/dist/packages/crucible/seeds/python/seed-052 3.py +0 -41
- package/dist/packages/crucible/seeds/ruby/seed-001 3.rb +0 -15
- package/dist/packages/crucible/seeds/ruby/seed-002 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-003 3.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-004 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-005 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-006 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-007 3.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-008 3.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-009 3.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-010 3.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-011 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-012 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-013 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-014 3.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-015 3.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-016 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-017 3.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-018 3.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-019 3.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-020 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-021 3.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-022 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-023 3.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-024 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-025 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-026 3.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-027 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-028 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-029 3.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-030 3.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-031 3.rb +0 -17
- package/dist/packages/crucible/seeds/ruby/seed-032 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-033 3.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-034 3.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-035 3.rb +0 -19
- package/dist/packages/crucible/seeds/ruby/seed-036 3.rb +0 -18
- package/dist/packages/crucible/seeds/ruby/seed-037 3.rb +0 -21
- package/dist/packages/crucible/seeds/ruby/seed-038 3.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-039 3.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-040 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-041 3.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-042 3.rb +0 -25
- package/dist/packages/crucible/seeds/ruby/seed-043 3.rb +0 -23
- package/dist/packages/crucible/seeds/ruby/seed-044 3.rb +0 -16
- package/dist/packages/crucible/seeds/ruby/seed-045 3.rb +0 -22
- package/dist/packages/crucible/seeds/ruby/seed-046 3.rb +0 -27
- package/dist/packages/crucible/seeds/ruby/seed-047 3.rb +0 -26
- package/dist/packages/crucible/seeds/ruby/seed-048 3.rb +0 -24
- package/dist/packages/crucible/seeds/ruby/seed-049 3.rb +0 -20
- package/dist/packages/crucible/seeds/ruby/seed-050 3.rb +0 -27
- package/dist/packages/crucible/seeds/rust/seed-001 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-002 3.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-003 3.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-004 3.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-005 3.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-006 3.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-007 3.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-008 3.rs +0 -19
- package/dist/packages/crucible/seeds/rust/seed-009 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-010 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-011 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-012 3.rs +0 -31
- package/dist/packages/crucible/seeds/rust/seed-013 3.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-014 3.rs +0 -30
- package/dist/packages/crucible/seeds/rust/seed-015 3.rs +0 -33
- package/dist/packages/crucible/seeds/rust/seed-016 3.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-017 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-018 3.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-019 3.rs +0 -36
- package/dist/packages/crucible/seeds/rust/seed-020 3.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-021 3.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-022 3.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-023 3.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-024 3.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-025 3.rs +0 -29
- package/dist/packages/crucible/seeds/rust/seed-026 3.rs +0 -23
- package/dist/packages/crucible/seeds/rust/seed-027 3.rs +0 -24
- package/dist/packages/crucible/seeds/rust/seed-028 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-029 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-030 3.rs +0 -30
- package/dist/packages/crucible/seeds/rust/seed-031 3.rs +0 -22
- package/dist/packages/crucible/seeds/rust/seed-032 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-033 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-034 3.rs +0 -20
- package/dist/packages/crucible/seeds/rust/seed-035 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-036 3.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-037 3.rs +0 -31
- package/dist/packages/crucible/seeds/rust/seed-038 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-039 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-040 3.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-041 3.rs +0 -32
- package/dist/packages/crucible/seeds/rust/seed-042 3.rs +0 -27
- package/dist/packages/crucible/seeds/rust/seed-043 3.rs +0 -29
- package/dist/packages/crucible/seeds/rust/seed-044 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-045 3.rs +0 -28
- package/dist/packages/crucible/seeds/rust/seed-046 3.rs +0 -25
- package/dist/packages/crucible/seeds/rust/seed-047 3.rs +0 -34
- package/dist/packages/crucible/seeds/rust/seed-048 3.rs +0 -21
- package/dist/packages/crucible/seeds/rust/seed-049 3.rs +0 -26
- package/dist/packages/crucible/seeds/rust/seed-050 3.rs +0 -23
- package/dist/packages/crucible/seeds/ts/seed-001 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-002 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-003 3.ts +0 -28
- package/dist/packages/crucible/seeds/ts/seed-004 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-005 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-006 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-007 3.ts +0 -28
- package/dist/packages/crucible/seeds/ts/seed-008 3.ts +0 -40
- package/dist/packages/crucible/seeds/ts/seed-009 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-010 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-011 3.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-012 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-013 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-014 3.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-015 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-016 3.ts +0 -37
- package/dist/packages/crucible/seeds/ts/seed-017 3.ts +0 -44
- package/dist/packages/crucible/seeds/ts/seed-018 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-019 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-020 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-021 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-022 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-023 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-024 3.ts +0 -35
- package/dist/packages/crucible/seeds/ts/seed-025 3.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-026 3.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-027 3.ts +0 -30
- package/dist/packages/crucible/seeds/ts/seed-028 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-029 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-030 3.ts +0 -37
- package/dist/packages/crucible/seeds/ts/seed-031 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-032 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-033 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-034 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-035 3.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-036 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-037 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-038 3.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-039 3.ts +0 -35
- package/dist/packages/crucible/seeds/ts/seed-040 3.ts +0 -31
- package/dist/packages/crucible/seeds/ts/seed-041 3.ts +0 -29
- package/dist/packages/crucible/seeds/ts/seed-042 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-043 3.ts +0 -32
- package/dist/packages/crucible/seeds/ts/seed-044 3.ts +0 -36
- package/dist/packages/crucible/seeds/ts/seed-045 3.ts +0 -39
- package/dist/packages/crucible/seeds/ts/seed-046 3.ts +0 -41
- package/dist/packages/crucible/seeds/ts/seed-047 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-048 3.ts +0 -33
- package/dist/packages/crucible/seeds/ts/seed-049 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-050 3.ts +0 -41
- package/dist/packages/crucible/seeds/ts/seed-051 3.ts +0 -34
- package/dist/packages/crucible/seeds/ts/seed-052 3.ts +0 -36
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: xss
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: ActionMailer HTML email built with unescaped user content
|
|
6
|
-
|
|
7
|
-
class NotificationMailer < ApplicationMailer
|
|
8
|
-
def welcome_email(user, custom_message)
|
|
9
|
-
@user = user
|
|
10
|
-
@message = custom_message
|
|
11
|
-
@subject = params[:subject] || "Welcome!"
|
|
12
|
-
|
|
13
|
-
mail(
|
|
14
|
-
to: @user.email,
|
|
15
|
-
subject: @subject
|
|
16
|
-
) do |format|
|
|
17
|
-
format.html { render html: "<h1>#{@user.name}</h1><p>#{@message}</p>".html_safe }
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: xss
|
|
3
|
-
# severity: medium
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: ERB template rendered with user string directly substituted
|
|
6
|
-
|
|
7
|
-
require 'erb'
|
|
8
|
-
|
|
9
|
-
def render_template(template_string, user_data)
|
|
10
|
-
ERB.new(template_string).result(binding)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def personalize_email(user_name, promo_code)
|
|
14
|
-
template = "Hello <%= user_name %>, your promo code is: <%= promo_code %>"
|
|
15
|
-
ERB.new(template).result(binding)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def build_page(title, body, footer)
|
|
19
|
-
tmpl = "<html><head><title><%= title %></title></head><body><%= body %><footer><%= footer %></footer></body></html>"
|
|
20
|
-
ERB.new(tmpl).result(binding)
|
|
21
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Backtick operator with user-supplied input — command injection
|
|
6
|
-
|
|
7
|
-
class SystemController < ApplicationController
|
|
8
|
-
def ping
|
|
9
|
-
host = params[:host]
|
|
10
|
-
result = `ping -c 4 #{host}`
|
|
11
|
-
render plain: result
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def dns_lookup
|
|
15
|
-
domain = params[:domain]
|
|
16
|
-
output = `nslookup #{domain}`
|
|
17
|
-
render plain: output
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: system() with user-controlled arguments — command injection
|
|
6
|
-
|
|
7
|
-
def convert_image(src, dest, width, height)
|
|
8
|
-
system("convert #{src} -resize #{width}x#{height} #{dest}")
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def compress_archive(dir_path, output_name)
|
|
12
|
-
system("tar -czf /tmp/#{output_name} #{dir_path}")
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def run_script(script_name, args)
|
|
16
|
-
system("/opt/scripts/#{script_name} #{args}")
|
|
17
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: exec() with user-controlled command — replaces process, no return
|
|
6
|
-
|
|
7
|
-
def execute_task(command)
|
|
8
|
-
exec(command)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def run_user_script(script_path, user_arg)
|
|
12
|
-
exec("ruby #{script_path} #{user_arg}")
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def start_service(service_name, config_file)
|
|
16
|
-
exec("#{service_name} --config #{config_file}")
|
|
17
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Open3.capture2 with shell string — user input in format string
|
|
6
|
-
|
|
7
|
-
require 'open3'
|
|
8
|
-
|
|
9
|
-
def run_ffmpeg(input_file, output_file, codec)
|
|
10
|
-
cmd = "ffmpeg -i '#{input_file}' -c:v #{codec} '#{output_file}'"
|
|
11
|
-
stdout, stderr, status = Open3.capture2(cmd)
|
|
12
|
-
{ output: stdout, error: stderr, success: status.success? }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def grep_logs(pattern, log_file)
|
|
16
|
-
stdout, _ = Open3.capture2("grep -n '#{pattern}' /var/log/#{log_file}")
|
|
17
|
-
stdout
|
|
18
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: IO.popen with user-controlled shell string
|
|
6
|
-
|
|
7
|
-
def tail_log(log_name, lines)
|
|
8
|
-
IO.popen("tail -n #{lines} /var/log/#{log_name}") do |f|
|
|
9
|
-
f.read
|
|
10
|
-
end
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def search_file(filename, keyword)
|
|
14
|
-
IO.popen("grep '#{keyword}' /data/#{filename}") do |io|
|
|
15
|
-
io.read
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def pipe_command(cmd)
|
|
20
|
-
IO.popen(cmd) { |f| f.read }
|
|
21
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: command_injection
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Kernel.system called with interpolated hostname for network diagnostics
|
|
6
|
-
|
|
7
|
-
class DiagnosticsController < ApplicationController
|
|
8
|
-
def check_connectivity
|
|
9
|
-
host = params[:host]
|
|
10
|
-
port = params[:port]
|
|
11
|
-
timeout = params[:timeout] || '5'
|
|
12
|
-
|
|
13
|
-
result = `nc -zv -w #{timeout} #{host} #{port} 2>&1`
|
|
14
|
-
render json: { output: result, host: host, port: port }
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def whois_lookup
|
|
18
|
-
domain = params[:domain]
|
|
19
|
-
output = `whois #{domain}`
|
|
20
|
-
render plain: output
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: path_traversal
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: File.read with unvalidated user-supplied path
|
|
6
|
-
|
|
7
|
-
class FilesController < ApplicationController
|
|
8
|
-
def show
|
|
9
|
-
filename = params[:filename]
|
|
10
|
-
content = File.read("/var/app/uploads/#{filename}")
|
|
11
|
-
render plain: content
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def download
|
|
15
|
-
path = params[:path]
|
|
16
|
-
data = File.read(path)
|
|
17
|
-
send_data data, filename: File.basename(path)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: path_traversal
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: send_file with user-controlled path — arbitrary file download
|
|
6
|
-
|
|
7
|
-
class DownloadsController < ApplicationController
|
|
8
|
-
def get_file
|
|
9
|
-
filename = params[:file]
|
|
10
|
-
base_dir = Rails.root.join('public', 'downloads')
|
|
11
|
-
file_path = File.join(base_dir, filename)
|
|
12
|
-
send_file file_path
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def export
|
|
16
|
-
name = params[:name]
|
|
17
|
-
path = "/var/reports/#{name}"
|
|
18
|
-
send_file path, type: 'application/octet-stream'
|
|
19
|
-
end
|
|
20
|
-
end
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: path_traversal
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Zip extraction writes files using entry name without sanitization — zip slip
|
|
6
|
-
|
|
7
|
-
require 'zip'
|
|
8
|
-
|
|
9
|
-
def extract_zip(zip_path, dest_dir)
|
|
10
|
-
Zip::File.open(zip_path) do |zip|
|
|
11
|
-
zip.each do |entry|
|
|
12
|
-
output_path = File.join(dest_dir, entry.name)
|
|
13
|
-
FileUtils.mkdir_p(File.dirname(output_path))
|
|
14
|
-
entry.extract(output_path) { true }
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: path_traversal
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: File.write to user-controlled destination path
|
|
6
|
-
|
|
7
|
-
def save_export(filename, data)
|
|
8
|
-
path = "/var/app/exports/#{filename}"
|
|
9
|
-
File.write(path, data)
|
|
10
|
-
path
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def cache_response(cache_key, content)
|
|
14
|
-
cache_path = "/tmp/cache/#{cache_key}"
|
|
15
|
-
FileUtils.mkdir_p(File.dirname(cache_path))
|
|
16
|
-
File.write(cache_path, content)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def write_template(template_name, subdir, content)
|
|
20
|
-
full_path = "/app/templates/#{subdir}/#{template_name}"
|
|
21
|
-
File.write(full_path, content)
|
|
22
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: path_traversal
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Dir.glob and File.open with user-controlled subdirectory
|
|
6
|
-
|
|
7
|
-
def list_files(base_dir, subdir)
|
|
8
|
-
Dir.glob("#{base_dir}/#{subdir}/*").map { |f| File.basename(f) }
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def read_config(config_name)
|
|
12
|
-
File.open("/etc/app/configs/#{config_name}") { |f| f.read }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def load_plugin(plugin_id)
|
|
16
|
-
manifest = File.read("./plugins/#{plugin_id}/manifest.json")
|
|
17
|
-
JSON.parse(manifest)
|
|
18
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: mass_assignment
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: params.permit(:all) or ActionController::Parameters without strong params
|
|
6
|
-
|
|
7
|
-
class UsersController < ApplicationController
|
|
8
|
-
def update
|
|
9
|
-
@user = User.find(params[:id])
|
|
10
|
-
@user.update(params[:user])
|
|
11
|
-
redirect_to @user
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def create
|
|
15
|
-
@user = User.new(params[:user].permit!)
|
|
16
|
-
@user.save
|
|
17
|
-
render json: @user
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: mass_assignment
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: ActiveRecord.new(params) without strong parameters — mass assignment
|
|
6
|
-
|
|
7
|
-
class ProfilesController < ApplicationController
|
|
8
|
-
def update
|
|
9
|
-
profile = Profile.find(params[:id])
|
|
10
|
-
profile.update_attributes(params[:profile])
|
|
11
|
-
render json: profile
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def create
|
|
15
|
-
profile = Profile.new(params)
|
|
16
|
-
profile.save!
|
|
17
|
-
render json: profile, status: :created
|
|
18
|
-
end
|
|
19
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: mass_assignment
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: attr_accessible :all or missing attr_protected — mass assignment in model
|
|
6
|
-
|
|
7
|
-
class User < ActiveRecord::Base
|
|
8
|
-
# No attr_protected or attr_accessible defined
|
|
9
|
-
# Allows setting admin, role, confirmed fields via mass assignment
|
|
10
|
-
|
|
11
|
-
def self.create_from_params(params)
|
|
12
|
-
create(params)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def self.register(registration_params)
|
|
16
|
-
new(registration_params).tap(&:save)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: mass_assignment
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: slice(:all) or no-arg permit! allowing all user-supplied attributes
|
|
6
|
-
|
|
7
|
-
class OrdersController < ApplicationController
|
|
8
|
-
def create
|
|
9
|
-
permitted = params.require(:order).permit!
|
|
10
|
-
@order = Order.create(permitted)
|
|
11
|
-
render json: @order
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def bulk_update
|
|
15
|
-
params[:records].each do |record_params|
|
|
16
|
-
item = Item.find(record_params[:id])
|
|
17
|
-
item.update(record_params.except(:id))
|
|
18
|
-
end
|
|
19
|
-
render json: { updated: params[:records].count }
|
|
20
|
-
end
|
|
21
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: mass_assignment
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Mongoid or other ODM with unfiltered hash assignment
|
|
6
|
-
|
|
7
|
-
require 'mongoid'
|
|
8
|
-
|
|
9
|
-
class Document
|
|
10
|
-
include Mongoid::Document
|
|
11
|
-
|
|
12
|
-
field :title, type: String
|
|
13
|
-
field :content, type: String
|
|
14
|
-
field :admin, type: Boolean, default: false
|
|
15
|
-
field :role, type: String, default: 'user'
|
|
16
|
-
|
|
17
|
-
def self.create_from_hash(attrs)
|
|
18
|
-
create!(attrs)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def update_from_request(request_params)
|
|
22
|
-
update_attributes!(request_params)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: ssrf
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Net::HTTP.get_response with user-controlled URL — SSRF
|
|
6
|
-
|
|
7
|
-
require 'net/http'
|
|
8
|
-
require 'uri'
|
|
9
|
-
|
|
10
|
-
class ProxyController < ApplicationController
|
|
11
|
-
def fetch
|
|
12
|
-
url = params[:url]
|
|
13
|
-
uri = URI.parse(url)
|
|
14
|
-
response = Net::HTTP.get_response(uri)
|
|
15
|
-
render plain: response.body
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def check_endpoint
|
|
19
|
-
target = params[:endpoint]
|
|
20
|
-
uri = URI(target)
|
|
21
|
-
res = Net::HTTP.get_response(uri)
|
|
22
|
-
render json: { status: res.code, body: res.body[0..200] }
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: ssrf
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: HTTParty.get with user-supplied URL — SSRF to internal services
|
|
6
|
-
|
|
7
|
-
require 'httparty'
|
|
8
|
-
|
|
9
|
-
class WebhookController < ApplicationController
|
|
10
|
-
def trigger
|
|
11
|
-
callback_url = params[:callback]
|
|
12
|
-
payload = { event: 'triggered', timestamp: Time.now.to_i }
|
|
13
|
-
HTTParty.post(callback_url, body: payload.to_json, headers: { 'Content-Type' => 'application/json' })
|
|
14
|
-
render json: { triggered: true }
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def preview_url
|
|
18
|
-
url = params[:url]
|
|
19
|
-
resp = HTTParty.get(url, timeout: 5)
|
|
20
|
-
render plain: resp.body
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: ssrf
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Faraday connection built from user-controlled base URL
|
|
6
|
-
|
|
7
|
-
require 'faraday'
|
|
8
|
-
|
|
9
|
-
class ImportController < ApplicationController
|
|
10
|
-
def import_feed
|
|
11
|
-
source_url = params[:feed_url]
|
|
12
|
-
conn = Faraday.new(url: source_url)
|
|
13
|
-
response = conn.get('/')
|
|
14
|
-
data = JSON.parse(response.body)
|
|
15
|
-
render json: { imported: data.size }
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def fetch_avatar
|
|
19
|
-
avatar_url = params[:avatar_url]
|
|
20
|
-
response = Faraday.get(avatar_url)
|
|
21
|
-
send_data response.body, type: 'image/jpeg'
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: ssrf
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: open-uri used to fetch user-supplied URL — SSRF including file:// scheme
|
|
6
|
-
|
|
7
|
-
require 'open-uri'
|
|
8
|
-
|
|
9
|
-
def fetch_remote_resource(url)
|
|
10
|
-
URI.open(url).read
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def download_image(image_url)
|
|
14
|
-
URI.open(image_url) do |f|
|
|
15
|
-
File.write('/tmp/downloaded_image', f.read)
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
class ResourceController < ApplicationController
|
|
20
|
-
def import
|
|
21
|
-
resource_url = params[:url]
|
|
22
|
-
content = URI.open(resource_url).read
|
|
23
|
-
render plain: content
|
|
24
|
-
end
|
|
25
|
-
end
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: ssrf
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: RestClient.get with user-supplied URL — SSRF
|
|
6
|
-
|
|
7
|
-
require 'rest-client'
|
|
8
|
-
|
|
9
|
-
class ProxyService
|
|
10
|
-
def self.relay(target_url, headers = {})
|
|
11
|
-
RestClient.get(target_url, headers)
|
|
12
|
-
rescue RestClient::Exception => e
|
|
13
|
-
e.response
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def self.post_webhook(url, payload)
|
|
17
|
-
RestClient.post(url, payload.to_json, content_type: :json)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def self.check_health(host)
|
|
21
|
-
RestClient.get("http://#{host}/health")
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: open_redirect
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: redirect_to params[:url] without validation — open redirect
|
|
6
|
-
|
|
7
|
-
class SessionsController < ApplicationController
|
|
8
|
-
def create
|
|
9
|
-
if authenticated?
|
|
10
|
-
redirect_to params[:url] || root_path
|
|
11
|
-
else
|
|
12
|
-
flash[:error] = 'Invalid credentials'
|
|
13
|
-
render :new
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: open_redirect
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: redirect_to with 'next' or 'return_to' parameter — open redirect after login
|
|
6
|
-
|
|
7
|
-
class AuthController < ApplicationController
|
|
8
|
-
def callback
|
|
9
|
-
next_url = params[:next] || params[:return_to] || dashboard_path
|
|
10
|
-
redirect_to next_url
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def logout
|
|
14
|
-
sign_out current_user
|
|
15
|
-
redirect_to params[:redirect]
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def sso_redirect
|
|
19
|
-
target = request.params['RelayState'] || root_url
|
|
20
|
-
redirect_to target, allow_other_host: true
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: open_redirect
|
|
3
|
-
# severity: medium
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: Regex-based redirect validation bypassable with CRLF or subdomain trick
|
|
6
|
-
|
|
7
|
-
class RedirectController < ApplicationController
|
|
8
|
-
SAFE_DOMAIN = 'example.com'
|
|
9
|
-
|
|
10
|
-
def go
|
|
11
|
-
target = params[:to]
|
|
12
|
-
if target =~ /example\.com/
|
|
13
|
-
redirect_to target, allow_other_host: true
|
|
14
|
-
else
|
|
15
|
-
redirect_to root_path
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def external
|
|
20
|
-
url = params[:url]
|
|
21
|
-
trusted = ['example.com', 'partner.com']
|
|
22
|
-
host = URI.parse(url).host rescue ''
|
|
23
|
-
if trusted.include?(host)
|
|
24
|
-
redirect_to url, allow_other_host: true
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: open_redirect
|
|
3
|
-
# severity: high
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: send_file or redirect after download with user-controlled path
|
|
6
|
-
|
|
7
|
-
class DownloadsController < ApplicationController
|
|
8
|
-
def complete
|
|
9
|
-
file = params[:file]
|
|
10
|
-
callback = params[:callback_url]
|
|
11
|
-
send_file Rails.root.join('public', 'downloads', file)
|
|
12
|
-
redirect_to callback
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def track_and_redirect
|
|
16
|
-
destination = params[:destination]
|
|
17
|
-
track_click(destination)
|
|
18
|
-
redirect_to destination, allow_other_host: true
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
private
|
|
22
|
-
|
|
23
|
-
def track_click(url)
|
|
24
|
-
ClickLog.create(url: url, user_id: current_user&.id)
|
|
25
|
-
end
|
|
26
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: yaml_deserialization
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: YAML.load with user-controlled input — allows arbitrary object instantiation
|
|
6
|
-
|
|
7
|
-
class ConfigController < ApplicationController
|
|
8
|
-
def import
|
|
9
|
-
config_data = params[:config]
|
|
10
|
-
config = YAML.load(config_data)
|
|
11
|
-
apply_config(config)
|
|
12
|
-
render json: { applied: true }
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def load_template(template_string)
|
|
16
|
-
YAML.load(template_string)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
def apply_config(cfg)
|
|
22
|
-
cfg.each { |k, v| Rails.application.config.send("#{k}=", v) rescue nil }
|
|
23
|
-
end
|
|
24
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: yaml_deserialization
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: YAML.load on file contents from user-supplied path — RCE via Psych gadget
|
|
6
|
-
|
|
7
|
-
def load_config_file(config_name)
|
|
8
|
-
path = "/etc/app/configs/#{config_name}.yml"
|
|
9
|
-
content = File.read(path)
|
|
10
|
-
YAML.load(content)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def parse_pipeline(pipeline_definition)
|
|
14
|
-
YAML.load(pipeline_definition)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def deserialize_job(job_yaml)
|
|
18
|
-
job = YAML.load(job_yaml)
|
|
19
|
-
job.merge(loaded_at: Time.now)
|
|
20
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# [CRUCIBLE-SEED]
|
|
2
|
-
# category: yaml_deserialization
|
|
3
|
-
# severity: critical
|
|
4
|
-
# language: ruby
|
|
5
|
-
# expected: YAML.load used in API endpoint — remote code execution via crafted payload
|
|
6
|
-
|
|
7
|
-
require 'sinatra'
|
|
8
|
-
require 'yaml'
|
|
9
|
-
|
|
10
|
-
post '/api/import' do
|
|
11
|
-
content_type :json
|
|
12
|
-
raw_body = request.body.read
|
|
13
|
-
data = YAML.load(raw_body)
|
|
14
|
-
process_import(data)
|
|
15
|
-
{ imported: data.keys.count }.to_json
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
post '/api/config/update' do
|
|
19
|
-
yaml_config = params[:yaml]
|
|
20
|
-
config = YAML.load(yaml_config)
|
|
21
|
-
config.each { |k, v| CONFIG[k] = v }
|
|
22
|
-
{ updated: true }.to_json
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def process_import(data)
|
|
26
|
-
data.each { |k, v| puts "#{k}: #{v}" }
|
|
27
|
-
end
|