# frozen_string_literal: true require_relative './environment.rb' describe '/admin' do include Capybara::DSL include Capybara::Minitest::Assertions before do Capybara.reset_sessions! @admin = Fabricate :site, is_admin: true page.set_rack_session id: @admin.id visit '/admin' end describe 'permissions' do include Capybara::DSL it 'works for admin site' do _(page.body).must_match /Admin/ end it 'requires csrf tokens for admin post routes' do site_to_ban = Fabricate :site page.driver.post '/admin/ban', { usernames: site_to_ban.username, classifier: '', ban_using_ips: '' } _(page.driver.status_code).must_equal 302 site_to_ban.reload _(site_to_ban.is_banned).must_equal false site_to_change = Fabricate :site, password: 'oldpass' page.driver.post '/admin/site/change_password', { username: site_to_change.username, new_password: 'newpass', new_password_confirm: 'newpass' } _(page.driver.status_code).must_equal 302 site_to_change.reload _(site_to_change.valid_password?('oldpass')).must_equal true _(site_to_change.valid_password?('newpass')).must_equal false end it 'blocks all /admin paths for non-admin users' do non_admin_site = Fabricate :site page.set_rack_session id: non_admin_site.id # Test GET routes ['/admin', '/admin/reports', '/admin/usage', '/admin/email', '/admin/stats', '/admin/masquerade/test'].each do |path| visit path _(page.current_path).must_equal '/', "Failed to block GET #{path}" end # Test POST routes ['/admin/reports', '/admin/ban', '/admin/unban', '/admin/mark_nsfw', '/admin/feature', '/admin/email', '/admin/site/change_password'].each do |path| page.driver.post path, {} _(page.driver.status_code).must_equal 302, "Expected redirect for POST #{path}" # Follow the redirect and verify we end up at home page (blocked) visit page.driver.response_headers['Location'] if page.driver.response_headers['Location'] _(page.current_path).must_equal '/', "POST #{path} should redirect to home page, not #{page.current_path}" end end it 'blocks /admin paths for signed out users' do page.set_rack_session id: nil visit '/admin' _(page.current_path).must_equal '/' page.driver.post '/admin/ban', {usernames: 'test'} _(page.driver.status_code).must_equal 302 # Follow the redirect and verify we end up at home page (blocked) visit page.driver.response_headers['Location'] if page.driver.response_headers['Location'] _(page.current_path).must_equal '/', "Signed out user POST should redirect to home page" end end describe 'supporter upgrade' do include Capybara::DSL it 'works for valid site' do site = Fabricate :site within(:css, '#upgradeToSupporter') do fill_in 'username', with: site.username click_button 'Upgrade to Supporter' site.reload _(site.stripe_customer_id).wont_be_nil _(site.stripe_subscription_id).wont_be_nil _(site.values[:plan_type]).must_equal 'special' _(site.supporter?).must_equal true end end end describe 'ban site form' do include Capybara::DSL it 'bans single site successfully' do site_to_ban = Fabricate :site fill_in 'usernames', with: site_to_ban.username # select 'Spam', from: 'classifier' click_button 'Ban' site_to_ban.reload _(site_to_ban.is_banned).must_equal true _(page.body).must_match(/sites have been banned/) end it 'bans multiple sites successfully' do site1 = Fabricate :site site2 = Fabricate :site fill_in 'usernames', with: "#{site1.username}\n#{site2.username}" #select 'Phishing', from: 'classifier' click_button 'Ban' site1.reload site2.reload _(site1.is_banned).must_equal true _(site2.is_banned).must_equal true _(page.body).must_match(/sites have been banned/) end it 'bans sites using IP when checkbox is checked' do ip_address = '192.168.1.1' site1 = Fabricate :site, ip: ip_address site2 = Fabricate :site, ip: ip_address fill_in 'usernames', with: site1.username check 'ban_using_ips' select 'Spam', from: 'classifier' click_button 'Ban' site1.reload site2.reload _(site1.is_banned).must_equal true _(site2.is_banned).must_equal true end end describe 'unban site form' do include Capybara::DSL before do @banned_site = Fabricate :site, is_banned: true end it 'unbans site successfully' do within(:css, 'form[action="/admin/unban"]') do fill_in 'username', with: @banned_site.username click_button 'Unban' end @banned_site.reload _(@banned_site.is_banned).must_equal false _(page.body).must_match(/was unbanned/) end it 'handles non-existent username gracefully' do within(:css, 'form[action="/admin/unban"]') do fill_in 'username', with: 'nonexistent_user' click_button 'Unban' end _(page.body).must_match(/User not found/) end end describe 'mark as NSFW form' do include Capybara::DSL it 'marks site as NSFW successfully' do site_to_mark = Fabricate :site within(:css, 'form[action="/admin/mark_nsfw"]') do fill_in 'username', with: site_to_mark.username click_button 'Mark NSFW' end site_to_mark.reload _(site_to_mark.is_nsfw).must_equal true _(page.body).must_match(/MISSION ACCOMPLISHED/) end it 'handles non-existent username gracefully' do within(:css, 'form[action="/admin/mark_nsfw"]') do fill_in 'username', with: 'nonexistent_user' click_button 'Mark NSFW' end _(page.body).must_match(/User not found/) end end describe 'feature site form' do include Capybara::DSL it 'features site successfully' do site_to_feature = Fabricate :site within(:css, '#featureSite') do fill_in 'username', with: site_to_feature.username click_button 'Feature Site' end site_to_feature.reload _(site_to_feature.featured_at).wont_be_nil _(page.body).must_match(/Site has been featured/) end it 'handles non-existent username gracefully' do within(:css, '#featureSite') do fill_in 'username', with: 'nonexistent_user' click_button 'Feature Site' end _(page.body).must_match(/User not found/) end end describe 'site info lookup' do include Capybara::DSL it 'finds sites by username, email, domain, and urls' do username_site = Fabricate :site, username: 'plainuser' email_site = Fabricate :site, username: 'emailuser', email: 'user@gmail.com' domain_site = Fabricate :site, username: 'domainsite', domain: 'domain.com' neocities_site = Fabricate :site, username: 'derp' [ ['plainuser', username_site.username], ['user@gmail.com', email_site.username], ['derp.neocities.org', neocities_site.username], ['domain.com', domain_site.username], ['https://domain.com/some/path', domain_site.username], ['https://derp.neocities.org/anything', neocities_site.username] ].each do |input, expected_username| visit "/admin/site/#{input}" _(page.body).must_match(/Site Info: #{Regexp.quote(expected_username)}/, "input #{input} current_path #{page.current_path}") end end it 'shows deleted and banned child sites in relationships' do parent_site = Fabricate :site active_child = Fabricate :site, parent_site_id: parent_site.id deleted_child = Fabricate :site, parent_site_id: parent_site.id, is_deleted: true banned_child = Fabricate :site, parent_site_id: parent_site.id, is_deleted: true, is_banned: true visit "/admin/site/#{parent_site.username}" _(page.body).must_match(/has 3 child sites/) _(page.body).must_match(/#{Regexp.escape(active_child.username)}/) _(page.body).must_match(/#{Regexp.escape(deleted_child.username)}/) _(page.body).must_match(/#{Regexp.escape(banned_child.username)}/) _(page.body).must_match(/\(deleted\)/) _(page.body).must_match(/\(banned\)/) _(page.body).must_match(/\s*4\s*<\/strong>\s*total sites/) _(page.body).must_match(/\b2 active\b/) _(page.body).must_match(/\b1 banned\b/) _(page.body).must_match(/\b1 deleted\b/) end it 'blurs page screenshots only for reported blur-category paths on the current site' do site = Fabricate :site reporting_site = Fabricate :site other_site = Fabricate :site blur_category = Array($config['moderation_blur_categories']).first site.store_files [ {filename: 'flagged.html', tempfile: Rack::Test::UploadedFile.new('./tests/files/index.html', 'text/html')}, {filename: 'clear.html', tempfile: Rack::Test::UploadedFile.new('./tests/files/index.html', 'text/html')} ] Report.create( site: site, reporting_site: reporting_site, type: blur_category, comments: 'Blur this page', site_file_path: 'flagged.html' ) Report.create( site: site, reporting_site: reporting_site, type: 'phishing', comments: 'Do not blur this page', site_file_path: 'clear.html' ) Report.create( site: other_site, reporting_site: reporting_site, type: blur_category, comments: 'Different site should not affect current page', site_file_path: 'clear.html' ) visit "/admin/site/#{site.username}" blurred_image = find("img[data-site-file-path='flagged.html']") clear_image = find("img[data-site-file-path='clear.html']") _(blurred_image[:style].to_s).must_match(/filter:\s*blur\(4px\)/) _(clear_image[:style].to_s).wont_match(/filter:\s*blur\(4px\)/) end it 'changes a parent site password' do EmailWorker.jobs.clear site = Fabricate :site, password: 'oldpass' visit "/admin/site/#{site.username}" within(:css, 'form[action="/admin/site/change_password"]') do fill_in 'new_password', with: 'newpass' fill_in 'new_password_confirm', with: 'newpass' click_button 'Change Password' end _(page).must_have_content(/Password changed/) site.reload _(site.valid_password?('oldpass')).must_equal false _(site.valid_password?('newpass')).must_equal true _(EmailWorker.jobs.select {|job| job['args'].first['subject'] =~ /password has been changed/i}.length).must_equal 1 end it 'does not show the password form on child site info pages' do parent_site = Fabricate :site, password: 'parentold' child_site = Fabricate :site, parent_site_id: parent_site.id, password: 'childold' visit "/admin/site/#{child_site.username}" _(page).wont_have_selector('form[action="/admin/site/change_password"]') end it 'does not change a password when a child site is posted directly' do parent_site = Fabricate :site, password: 'parentold' child_site = Fabricate :site, parent_site_id: parent_site.id, password: 'childold' visit "/admin/site/#{parent_site.username}" token = find('form[action="/admin/site/change_password"] input[name="csrf_token"]', visible: false).value page.driver.post '/admin/site/change_password', { username: child_site.username, new_password: 'newpass', new_password_confirm: 'newpass', csrf_token: token } parent_site.reload child_site.reload _(parent_site.valid_password?('parentold')).must_equal true _(child_site.valid_password?('childold')).must_equal true _(child_site.valid_password?('newpass')).must_equal false end it 'does not change a password when confirmation does not match' do site = Fabricate :site, password: 'oldpass' visit "/admin/site/#{site.username}" within(:css, 'form[action="/admin/site/change_password"]') do fill_in 'new_password', with: 'newpass' fill_in 'new_password_confirm', with: 'different' click_button 'Change Password' end _(page).must_have_content(/New passwords do not match/) site.reload _(site.valid_password?('oldpass')).must_equal true _(site.valid_password?('newpass')).must_equal false end end describe 'email blasting' do before do EmailWorker.jobs.clear @admin_site = Fabricate :site, is_admin: true end it 'works' do DB['update sites set changed_count=?', 0].first relevant_emails = [] sites_emailed_count = Site::EMAIL_BLAST_MAXIMUM_PER_DAY*2 sites_emailed_count.times { site = Fabricate :site, updated_at: Time.now, changed_count: 1 relevant_emails << site.email } EmailWorker.jobs.clear time = Time.now Timecop.freeze(time) do visit '/admin/email' fill_in 'subject', with: 'Subject Test' fill_in 'body', with: 'Body Test' click_button 'Send' relevant_jobs = EmailWorker.jobs.select{|j| relevant_emails.include?(j['args'].first['to']) } _(relevant_jobs.length).must_equal sites_emailed_count relevant_jobs.each do |job| args = job['args'].first _(args['from']).must_equal 'Neocities ' _(args['subject']).must_equal 'Subject Test' _(args['body']).must_equal 'Body Test' end _(relevant_jobs.select {|j| j['at'].nil? || j['at'] == Time.now.to_f}.length).must_equal 1 _(relevant_jobs.select {|j| j['at'] == (Time.now + 0.5).to_f}.length).must_equal 1 _(relevant_jobs.select {|j| j['at'] == (time+1.day.to_i).to_f}.length).must_equal 1 _(relevant_jobs.select {|j| j['at'] == (time+1.day.to_i+0.5).to_f}.length).must_equal 1 end end end describe '/admin/reports' do include Capybara::DSL before do Capybara.current_driver = :selenium_chrome_headless_largewindow @admin = Fabricate :site, is_admin: true page.set_rack_session id: @admin.id @reported_site = Fabricate :site @reporting_site = Fabricate :site @report = Report.create( site: @reported_site, reporting_site: @reporting_site, type: 'inappropriate', comments: 'Test report comment', site_file_path: 'test.html' ) end after do Capybara.use_default_driver end it 'displays reports page' do visit '/admin/reports' _(page.body).must_match /Site Reports/ _(page.body).must_match @reported_site.username _(page.body).must_match /inappropriate/i _(page.body).must_match /Test report comment/ end it 'displays anonymous reports without reporter' do # Clear existing reports to avoid interference Report.where(site: @reported_site).destroy anonymous_report = Report.create( site: @reported_site, reporting_site: nil, type: 'spam', comments: 'Anonymous report unique text' ) visit '/admin/reports' _(page.body).must_match /Anonymous report unique text/ within("#report-#{anonymous_report.id}") do _(page).wont_have_content 'reported by' end end it 'handles reports without site_file_path (defaults to index.html)' do no_path_report = Report.create( site: @reported_site, reporting_site: @reporting_site, type: 'phishing', comments: 'No specific file path', site_file_path: nil ) visit '/admin/reports' _(page.body).must_match /No specific file path/ _(page.body).must_match /index\.html/ end it 'handles reports with empty site_file_path' do empty_path_report = Report.create( site: @reported_site, reporting_site: @reporting_site, type: 'malware', comments: 'Empty file path', site_file_path: '' ) visit '/admin/reports' _(page.body).must_match /Empty file path/ _(page.body).must_match /index\.html/ end it 'filters out banned sites' do banned_site = Fabricate :site, is_banned: true banned_report = Report.create( site: banned_site, reporting_site: @reporting_site, type: 'spam', comments: 'Should not appear' ) visit '/admin/reports' _(page.body).wont_match banned_site.username _(page.body).wont_match /Should not appear/ end it 'filters out deleted sites' do deleted_site = Fabricate :site, is_deleted: true deleted_report = Report.create( site: deleted_site, reporting_site: @reporting_site, type: 'phishing', comments: 'Should not appear either' ) visit '/admin/reports' _(page.body).wont_match deleted_site.username _(page.body).wont_match /Should not appear either/ end it 'shows action buttons for each report' do visit '/admin/reports' within("#report-#{@report.id}") do _(page).must_have_button 'Ban Site' _(page).must_have_button 'Mark NSFW' _(page).must_have_button 'Dismiss' end end it 'hides Mark NSFW button for already NSFW sites' do @reported_site.update(is_nsfw: true) visit '/admin/reports' within("#report-#{@report.id}") do _(page).must_have_button 'Ban Site' _(page).wont_have_button 'Mark NSFW' _(page).must_have_button 'Dismiss' end end end end