diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 0bcaba3a057..8979c9724a5 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -6,11 +6,16 @@ Dangerfile @department-of-veterans-affairs/backend-review-group Dockerfile @department-of-veterans-affairs/backend-review-group Dockerfile-k8s @department-of-veterans-affairs/backend-review-group -docker-compose* @department-of-veterans-affairs/backend-review-group +docker-compose.yml @department-of-veterans-affairs/backend-review-group +docker-compose-clamav.yml @department-of-veterans-affairs/backend-review-group +docker-compose-deps.yml @department-of-veterans-affairs/backend-review-group +docker-compose.review.yml @department-of-veterans-affairs/backend-review-group +docker-compose.test.yml @department-of-veterans-affairs/backend-review-group Gemfile @department-of-veterans-affairs/backend-review-group Gemfile.lock @department-of-veterans-affairs/backend-review-group Jenkinsfile @department-of-veterans-affairs/backend-review-group Makefile @department-of-veterans-affairs/backend-review-group +Procfile @department-of-veterans-affairs/backend-review-group .devcontainer @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/cto-engineers app/controllers/appeals_base_controller.rb @department-of-veterans-affairs/backend-review-group app/controllers/appeals_base_controller_v1.rb @department-of-veterans-affairs/backend-review-group @@ -639,13 +644,13 @@ app/sidekiq/vbms @department-of-veterans-affairs/benefits-dependents-management app/sidekiq/vre/create_ch31_submissions_report_job.rb @department-of-veterans-affairs/benefits-non-disability @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/sidekiq/vre/submit1900_job.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group app/sidekiq/webhooks @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group -bin/fake_clamdscan @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/git_blame @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/rails @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/rake @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/rspec @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/setup @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group bin/sidekiq_quiet @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +clamav_tmp @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/application.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/betamocks @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/betamocks/services_config.yml @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -706,7 +711,7 @@ config/initializers/backtrace_silencers.rb @department-of-veterans-affairs/va-ap config/initializers/betamocks.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/initializers/bgs.rb @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/initializers/breakers.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group -config/initializers/clamscan.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +config/initializers/clamav.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/initializers/config.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/initializers/cookie_rotation.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group config/initializers/covid_vaccine_facilities.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/long-covid @@ -803,6 +808,7 @@ lib/caseflow @department-of-veterans-affairs/lighthouse-banana-peels @department lib/central_mail @department-of-veterans-affairs/lighthouse-banana-peels @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/chip @department-of-veterans-affairs/vsa-healthcare-health-quest-1-backend @department-of-veterans-affairs/patient-check-in @department-of-veterans-affairs/backend-review-group lib/claim_letters @department-of-veterans-affairs/benefits-management-tools-be @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +lib/clamav @department-of-veterans-affairs/backend-review-group lib/common/client/base.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/common/client/concerns/mhv_fhir_session_client.rb @department-of-veterans-affairs/vfs-mhv-medical-records @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/common/client/concerns/mhv_jwt_session_client.rb @department-of-veterans-affairs/vfs-mhv-medical-records @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -814,6 +820,7 @@ lib/common/client/middleware/request/remove_cookies.rb @department-of-veterans-a lib/common/client/middleware/response/soap_parser.rb @department-of-veterans-affairs/backend-review-group lib/common/exceptions/open_id_service_error.rb @department-of-veterans-affairs/lighthouse-pivot lib/common/file_helpers.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group +lib/common/virus_scan.rb @department-of-veterans-affairs/backend-review-group lib/debt_management_center @department-of-veterans-affairs/vsa-debt-resolution @department-of-veterans-affairs/backend-review-group lib/decision_review @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group lib/decision_review_v1 @department-of-veterans-affairs/Benefits-Team-1 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group @@ -920,6 +927,7 @@ lib/search @department-of-veterans-affairs/va-api-engineers @department-of-veter lib/sentry @department-of-veterans-affairs/backend-review-group lib/sentry_logging.rb @department-of-veterans-affairs/backend-review-group lib/sftp_writer @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers +lib/shrine @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers lib/sidekiq/attr_package.rb @department-of-veterans-affairs/octo-identity @department-of-veterans-affairs/backend-review-group lib/sidekiq/error_tag.rb @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers lib/sidekiq/form526_backup_submission_process @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/backend-review-group @department-of-veterans-affairs/va-api-engineers @@ -1386,6 +1394,7 @@ spec/lib/sentry @department-of-veterans-affairs/va-api-engineers @department-of- spec/lib/sftp_writer @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/lib/sftp_writer/factory_spec.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/lib/sftp_writer/remote_spec.rb @department-of-veterans-affairs/backend-review-group +spec/lib/shrine @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/lib/sidekiq/attr_package_spec.rb @department-of-veterans-affairs/octo-identity @department-of-veterans-affairs/backend-review-group spec/lib/sidekiq/error_tag_spec.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group spec/lib/sidekiq/form526_backup_submission_process @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/dbex-trex @department-of-veterans-affairs/benefits-disability-2 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group diff --git a/.github/workflows/audit_service_tags.yml b/.github/workflows/audit_service_tags.yml index b81aad522b3..9391b69f875 100644 --- a/.github/workflows/audit_service_tags.yml +++ b/.github/workflows/audit_service_tags.yml @@ -36,10 +36,9 @@ jobs: uses: docker/build-push-action@v5 with: build-args: | - sidekiq_license=${{ env.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} - userid=${{ env.VETS_API_USER_ID }} + BUNDLE_ENTERPRISE__CONTRIBSYS__COM=${{ env.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} + USER_ID=${{ env.VETS_API_USER_ID }} context: . - target: builder push: false load: true tags: vets-api @@ -48,8 +47,8 @@ jobs: - name: Setup Database run: | - docker-compose -f docker-compose.test.yml run vets-api bash \ - -c "CI=true RAILS_ENV=test DISABLE_BOOTSNAP=true parallel_test -n 13 -e 'bin/rails db:reset'" + docker-compose -f docker-compose.test.yml run web bash \ + -c "CI=true RAILS_ENV=test DISABLE_BOOTSNAP=true bundle exec parallel_test -n 13 -e 'bin/rails db:reset'" - name: Get changed files run: | @@ -60,6 +59,6 @@ jobs: - name: Run service tags audit controllers task run: | - docker-compose -f docker-compose.test.yml run -e CHANGED_FILES=${{ env.CHANGED_FILES }} vets-api bash \ + docker-compose -f docker-compose.test.yml run -e CHANGED_FILES=${{ env.CHANGED_FILES }} web bash \ -c "CI=true DISABLE_BOOTSNAP=true bundle exec rake service_tags:audit_controllers_ci" diff --git a/.github/workflows/code_checks.yml b/.github/workflows/code_checks.yml index 1361e44f06d..fc904fdbbb8 100644 --- a/.github/workflows/code_checks.yml +++ b/.github/workflows/code_checks.yml @@ -54,10 +54,9 @@ jobs: uses: docker/build-push-action@v5 with: build-args: | - sidekiq_license=${{ env.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} - userid=${{ env.VETS_API_USER_ID }} + BUNDLE_ENTERPRISE__CONTRIBSYS__COM=${{ env.BUNDLE_ENTERPRISE__CONTRIBSYS__COM }} + USER_ID=${{ env.VETS_API_USER_ID }} context: . - target: builder push: false load: true tags: vets-api @@ -66,13 +65,13 @@ jobs: - name: Setup Database run: | - docker-compose -f docker-compose.test.yml run vets-api bash \ - -c "CI=true RAILS_ENV=test DISABLE_BOOTSNAP=true parallel_test -n 13 -e 'bin/rails db:reset'" + docker-compose -f docker-compose.test.yml run web bash \ + -c "CI=true RAILS_ENV=test DISABLE_BOOTSNAP=true bundle exec parallel_test -n 13 -e 'bin/rails db:reset'" - name: Run Specs timeout-minutes: 20 run: | - docker-compose -f docker-compose.test.yml run vets-api bash \ + docker-compose -f docker-compose.test.yml run web bash \ -c "CI=true DISABLE_BOOTSNAP=true bundle exec parallel_rspec spec/ modules/ -n 13 -o '--color --tty'" - name: Upload Coverage Report diff --git a/.gitignore b/.gitignore index 42aad621156..1faef9f5952 100644 --- a/.gitignore +++ b/.gitignore @@ -102,3 +102,7 @@ node_modules # Ignore public folder (used for local document uploads) public +# Ignore any files within clamav_tmp + +clamav_tmp/* +!/clamav_tmp/.keep diff --git a/Dockerfile b/Dockerfile index 80f2ad21a87..2d403bc889e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,4 @@ -FROM ruby:3.2.4-slim-bookworm as rubyimg - -# XXX: using stretch here for pdftk dep, which is not availible after -# stretch (or in alpine) and is switched automatically to pdftk-java in buster -# https://github.com/department-of-veterans-affairs/va.gov-team/issues/3032 - +FROM ruby:3.2.4-slim-bookworm AS rubyimg FROM rubyimg AS modules WORKDIR /tmp @@ -13,96 +8,64 @@ COPY modules/ modules/ RUN find modules -type f ! \( -name Gemfile -o -name "*.gemspec" -o -path "*/lib/*/version.rb" \) -delete && \ find modules -type d -empty -delete -### -# shared build/settings for all child images, reuse these layers yo -### -FROM rubyimg AS base +FROM rubyimg + +# Allow for setting ENV vars via --build-arg +ARG BUNDLE_ENTERPRISE__CONTRIBSYS__COM \ + RAILS_ENV=development \ + USER_ID=1000 +ENV RAILS_ENV=$RAILS_ENV \ + BUNDLE_ENTERPRISE__CONTRIBSYS__COM=$BUNDLE_ENTERPRISE__CONTRIBSYS__COM \ + BUNDLER_VERSION=2.4.9 + +RUN groupadd --gid $USER_ID nonroot \ + && useradd --uid $USER_ID --gid nonroot --shell /bin/bash --create-home nonroot --home-dir /app + +WORKDIR /app -ARG userid=993 -SHELL ["/bin/bash", "-c"] -RUN groupadd -g $userid -r vets-api && \ - useradd -u $userid -r -m -d /srv/vets-api -g vets-api vets-api RUN apt-get update --fix-missing -RUN DEBIAN_FRONTEND=noninteractive apt-get install -y ca-certificates-java && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y dumb-init imagemagick pdftk poppler-utils curl \ - libpq5 vim libboost-all-dev clamav clamdscan clamav-daemon - -# The pki work below is for parity with the non-docker BRD deploys to mount certs into -# the container, we need to get rid of it and refactor the configuration bits into -# something more continer friendly in a later bunch of work -RUN mkdir -p /srv/vets-api/{clamav/database,pki/tls,secure,src} && \ - chown -R vets-api:vets-api /srv/vets-api && \ - ln -s /srv/vets-api/pki /etc/pki -# XXX: get rid of the CA trust manipulation when we have a better model for it -COPY config/ca-trust/* /usr/local/share/ca-certificates/ -# rename .pem files to .crt because update-ca-certificates ignores files that are not .crt -RUN cd /usr/local/share/ca-certificates ; for i in *.pem ; do mv $i ${i/pem/crt} ; done ; update-ca-certificates +RUN apt-get install -y poppler-utils build-essential libpq-dev git curl wget ca-certificates-java file \ + imagemagick pdftk \ + && apt-get clean \ + && rm -rf /var/cache/apt/archives/* /var/lib/apt/lists/* /tmp/* /var/tmp/* + # Relax ImageMagick PDF security. See https://stackoverflow.com/a/59193253. RUN sed -i '/rights="none" pattern="PDF"/d' /etc/ImageMagick-6/policy.xml -WORKDIR /srv/vets-api/src - -### -# dev stage; use --target=development to stop here -# Be sure to pass required ARGs as `--build-arg` -# This stage useful for mounting your local checkout with compose -# into the container to dev against. -### -FROM base AS development - -ARG sidekiq_license -ARG rails_env=development - -ENV BUNDLE_ENTERPRISE__CONTRIBSYS__COM=$sidekiq_license -ENV RAILS_ENV=$rails_env -ENV BUNDLER_VERSION=2.4.9 - -# only extra dev/build opts go here, common packages go in base 👆 -RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \ - git build-essential libxml2-dev libxslt-dev libpq-dev -COPY --chown=vets-api:vets-api config/freshclam.conf docker-entrypoint.sh ./ -USER vets-api -# XXX: this is tacky -RUN freshclam --config-file freshclam.conf -RUN gem install vtk -ENTRYPOINT ["/usr/bin/dumb-init", "--", "./docker-entrypoint.sh"] + + +# Install fwdproxy.crt into trust store +# Relies on update-ca-certificates being run in following step +COPY config/ca-trust/*.crt /usr/local/share/ca-certificates/ + +# Download VA Certs +COPY ./import-va-certs.sh . +RUN ./import-va-certs.sh + +COPY config/clamd.conf /etc/clamav/clamd.conf + +RUN mkdir -p /clamav_tmp && \ + chown -R nonroot:nonroot /clamav_tmp && \ + chmod 777 /clamav_tmp + + +ENV LANG=C.UTF-8 \ + BUNDLE_JOBS=4 \ + BUNDLE_PATH=/usr/local/bundle/cache \ + BUNDLE_RETRY=3 + RUN gem install bundler:${BUNDLER_VERSION} --no-document -### -# build stage; use --target=builder to stop here -# Also be sure to add build-args from development stage above -# -# This is development with the app copied in and built. The build results are used in -# prod below, but also useful if you want to have a container with the app and not -# mount your local checkout. -### -FROM development AS builder -# XXX: move modules/ to seperate repos so we can only copy Gemfile* and install a slim layer -ARG bundler_opts - -COPY --chown=vets-api:vets-api Gemfile Gemfile.lock ./ -COPY --chown=vets-api:vets-api --from=modules /tmp/modules modules/ - -RUN bundle install --binstubs="${BUNDLE_APP_CONFIG}/bin" $bundler_opts \ +COPY --from=modules /tmp/modules modules/ +COPY Gemfile Gemfile.lock ./ +RUN bundle install \ && rm -rf /usr/local/bundle/cache/*.gem \ && find /usr/local/bundle/gems/ -name "*.c" -delete \ && find /usr/local/bundle/gems/ -name "*.o" -delete \ && find /usr/local/bundle/gems/ -name ".git" -type d -prune -execdir rm -rf {} + +COPY --chown=nonroot:nonroot . . + +EXPOSE 3000 + +USER nonroot -COPY --chown=vets-api:vets-api . . -USER vets-api - -### -# prod stage; default if no target given -# to build prod you probably want options like below to get a good build -# --build-arg sidekiq_license="$BUNDLE_ENTERPRISE__CONTRIBSYS__COM" --build-arg rails_env=production --build-arg bundler_opts="--no-cache --without development test" -# This inherits from base again to avoid bringing in extra built time binary packages -### -FROM base AS production - -ENV RAILS_ENV=production -COPY --from=builder $BUNDLE_APP_CONFIG $BUNDLE_APP_CONFIG -COPY --from=builder --chown=vets-api:vets-api /srv/vets-api/src ./ -COPY --from=builder --chown=vets-api:vets-api /srv/vets-api/clamav/database ../clamav/database -RUN if [ -d certs-tmp ] ; then cd certs-tmp ; for i in * ; do cp $i /usr/local/share/ca-certificates/${i/pem/crt} ; done ; fi && update-ca-certificates -USER vets-api -ENTRYPOINT ["/usr/bin/dumb-init", "--", "./docker-entrypoint.sh"] +CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"] diff --git a/Gemfile b/Gemfile index f3a4bf79ce5..ce4bd571ea4 100644 --- a/Gemfile +++ b/Gemfile @@ -55,7 +55,7 @@ gem 'bootsnap', require: false gem 'breakers' gem 'carrierwave' gem 'carrierwave-aws' -gem 'clam_scan' +gem 'clamav-client', require: 'clamav/client' gem 'combine_pdf' gem 'config' gem 'connect_vbms', git: 'https://github.com/adhocteam/connect_vbms', tag: 'v2.0.0.rc', require: 'vbms' diff --git a/Gemfile.lock b/Gemfile.lock index 465033cee09..e3cf8f240a5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -301,7 +301,7 @@ GEM cork nap open4 (~> 1.3) - clam_scan (0.0.2) + clamav-client (3.2.0) cliver (0.3.2) coderay (1.1.3) coercible (1.0.0) @@ -596,9 +596,12 @@ GEM kramdown (~> 2.0) language_server-protocol (3.17.0.3) libdatadog (5.0.0.1.0) + libdatadog (5.0.0.1.0-aarch64-linux) libdatadog (5.0.0.1.0-x86_64-linux) libddwaf (1.14.0.0.0) ffi (~> 1.0) + libddwaf (1.14.0.0.0-aarch64-linux) + ffi (~> 1.0) libddwaf (1.14.0.0.0-java) ffi (~> 1.0) libddwaf (1.14.0.0.0-x86_64-linux) @@ -1076,6 +1079,7 @@ GEM zeitwerk (2.6.13) PLATFORMS + aarch64-linux java ruby x64-mingw32 @@ -1111,7 +1115,7 @@ DEPENDENCIES carrierwave-aws check_in! claims_api! - clam_scan + clamav-client combine_pdf config connect_vbms! diff --git a/Makefile b/Makefile index 16c7d952307..cac52538ac9 100644 --- a/Makefile +++ b/Makefile @@ -7,16 +7,9 @@ else ENV_ARG := dev endif -ifdef clam - FOREMAN_ARG := all=1 -else - FOREMAN_ARG := all=1,clamd=0,freshclam=0 -endif - - COMPOSE_DEV := docker-compose COMPOSE_TEST := docker-compose -f docker-compose.test.yml -BASH := run --rm --service-ports vets-api bash +BASH := run --rm --service-ports web bash BASH_DEV := $(COMPOSE_DEV) $(BASH) -c BASH_TEST := $(COMPOSE_TEST) $(BASH) --login -c SPEC_PATH := spec/ modules/ @@ -117,9 +110,9 @@ spec: ## Runs spec tests .PHONY: spec_parallel_setup spec_parallel_setup: ## Setup the parallel test dbs. This resets the current test db, as well as the parallel test dbs ifeq ($(ENV_ARG), dev) - @$(BASH_DEV) "RAILS_ENV=test DISABLE_BOOTSNAP=true parallel_test -e 'bundle exec rake db:reset'" + @$(BASH_DEV) "RAILS_ENV=test DISABLE_BOOTSNAP=true bundle exec parallel_test -e 'bundle exec rake db:reset db:migrate'" else - @$(COMPOSE_TEST) $(BASH) -c "RAILS_ENV=test DISABLE_BOOTSNAP=true parallel_test -e 'bundle exec rake db:reset'" + @$(COMPOSE_TEST) $(BASH) -c "RAILS_ENV=test DISABLE_BOOTSNAP=true parallel_test -e 'bundle exec rake db:reset db:migrate'" endif .PHONY: spec_parallel @@ -131,14 +124,14 @@ else endif .PHONY: up -up: db ## Starts the server and associated services with docker-compose, use `clam=1 make up` to run ClamAV - @$(BASH_DEV) "rm -f tmp/pids/server.pid && foreman start -m ${FOREMAN_ARG}" +up: db ## Starts the server and associated services with docker-compose + @$(BASH_DEV) "rm -f tmp/pids/server.pid && foreman start -m all=1" # NATIVE COMMANDS .PHONY: native-up native-up: bundle install - foreman start -m ${FOREMAN_ARG} + foreman start -m all=1 .PHONY: native-lint native-lint: diff --git a/Procfile b/Procfile index 074c2758193..9af216e5db4 100644 --- a/Procfile +++ b/Procfile @@ -1,4 +1,2 @@ web: bundle exec puma -p 3000 -C ./config/puma.rb job: bundle exec sidekiq -q critical,4 -q tasker,3 -q default,2 -q low,1 -freshclam: /usr/bin/freshclam -d --config-file=config/freshclam.conf -clamd: /usr/sbin/clamd -c config/clamd.conf diff --git a/app/uploaders/uploader_virus_scan.rb b/app/uploaders/uploader_virus_scan.rb index 4bd293a83d7..846adc6d175 100644 --- a/app/uploaders/uploader_virus_scan.rb +++ b/app/uploaders/uploader_virus_scan.rb @@ -16,13 +16,14 @@ class VirusFoundError < StandardError def validate_virus_free(file) return unless Rails.env.production? - temp_file_path = Common::FileHelpers.generate_temp_file(file.read) + temp_file_path = Common::FileHelpers.generate_clamav_temp_file(file.read) result = Common::VirusScan.scan(temp_file_path) File.delete(temp_file_path) - unless result.safe? + # Common::VirusScan result will return true or false + unless result # unless safe file.delete - raise VirusFoundError, result.body + raise VirusFoundError, "Virus Found + #{temp_file_path}" end end end diff --git a/bin/fake_clamdscan b/bin/fake_clamdscan deleted file mode 100755 index 63a31f0b72c..00000000000 --- a/bin/fake_clamdscan +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -# frozen_string_literal: true - -echo 'Fake scanning file with fake_clamdscan script' -exit 0 diff --git a/clamav_tmp/.keep b/clamav_tmp/.keep new file mode 100644 index 00000000000..e69de29bb2d diff --git a/config/clamd.conf b/config/clamd.conf index 52595bda7b5..1c12245cf51 100644 --- a/config/clamd.conf +++ b/config/clamd.conf @@ -1,5 +1,7 @@ Foreground yes -DatabaseDirectory /srv/vets-api/clamav/database -LocalSocket /srv/vets-api/clamav/clamd.ctl TCPSocket 3310 -TCPAddr 127.0.0.1 \ No newline at end of file +TCPAddr 127.0.0.1 + +LogSyslog yes +LogVerbose yes +ExtendedDetectionInfo yes diff --git a/config/freshclam.conf b/config/freshclam.conf index fa18e68d88b..f43853975e0 100644 --- a/config/freshclam.conf +++ b/config/freshclam.conf @@ -3,5 +3,5 @@ PidFile /srv/vets-api/clamav/freshclam.pid Checks 8 DatabaseDirectory /srv/vets-api/clamav/database PrivateMirror dsva-vetsgov-utility-clamav.s3-us-gov-west-1.amazonaws.com -NotifyClamd /srv/vets-api/src/config/clamd.conf +NotifyClamd /app/config/clamd.conf ReceiveTimeout 600 diff --git a/config/initializers/clamav.rb b/config/initializers/clamav.rb new file mode 100644 index 00000000000..bc13ae935c3 --- /dev/null +++ b/config/initializers/clamav.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +if Rails.env.development? + ENV['CLAMD_TCP_HOST'] = Settings.clamav.host + ENV['CLAMD_TCP_PORT'] = Settings.clamav.port +end diff --git a/config/initializers/clamscan.rb b/config/initializers/clamscan.rb deleted file mode 100644 index 2f864ddfb9a..00000000000 --- a/config/initializers/clamscan.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -ClamScan.configure do |clam_config| - clam_config.client_location = Settings.binaries.clamdscan -end diff --git a/config/settings.local.yml.example b/config/settings.local.yml.example index ad55d272e4a..c9db66102e6 100644 --- a/config/settings.local.yml.example +++ b/config/settings.local.yml.example @@ -6,11 +6,11 @@ # The relative path to department-of-veterans-affairs/vets-api-mockdata # cache_dir: ../vets-api-mockdata -# binaries: - # For NATIVE and DOCKER installation +# clamav: # A "virus scanner" that always returns success for development purposes - # NOTE: You may need to specify a full path instead of a relative path - # clamdscan: ./bin/fake_clamdscan + # mock: true + # host: '0.0.0.0' + # port: '33100' # NOTE: This file is excluded by railsconfig in the test env. # Use config/settings/test.local.yml instead. diff --git a/config/settings.yml b/config/settings.yml index c1cd8bdb74f..e3f5af2d20c 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -97,6 +97,13 @@ binaries: pdftk: pdftk clamdscan: /usr/bin/clamdscan +clamav: + mock: false + # host & port here are only used in development here: + # config/initializers/clamav.rb + host: 'clamav' + port: '3310' + db_encryption_key: f01ff8ebd1a2b053ad697ae1f0d86adb database_url: postgis:///vets-api diff --git a/docker-compose-clamav.yml b/docker-compose-clamav.yml new file mode 100644 index 00000000000..239aa2936a3 --- /dev/null +++ b/docker-compose-clamav.yml @@ -0,0 +1,10 @@ +version: '3.4' +services: + clamav: + volumes: + - shared-vol:/vets-api + image: clamav/clamav + ports: + - 33100:3310 +volumes: + shared-vol: diff --git a/docker-compose-deps.yml b/docker-compose-deps.yml index b1ab99a4d31..489576324b8 100644 --- a/docker-compose-deps.yml +++ b/docker-compose-deps.yml @@ -13,4 +13,11 @@ services: - ./data:/var/lib/postgresql/data:cached ports: - "54320:5432" - + clamav: + volumes: + - shared-vol:/vets-api + image: clamav/clamav + ports: + - 33100:3310 +volumes: + shared-vol: diff --git a/docker-compose.review.yml b/docker-compose.review.yml index 43769083ab7..4ce4b1999f9 100644 --- a/docker-compose.review.yml +++ b/docker-compose.review.yml @@ -1,55 +1,70 @@ version: '3.4' + +x-app: &common + build: + args: + BUNDLE_ENTERPRISE__CONTRIBSYS__COM: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" + USER_ID: ${VETS_API_USER_ID} + context: . + environment: + RAILS_ENV: development + BUNDLE_ENTERPRISE__CONTRIBSYS__COM: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" + "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" + "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}" + "Settings.redis.app_data.url": "redis://redis:6379" + "Settings.redis.sidekiq.url": "redis://redis:6379" + "Settings.redis.rails_cache.url": "redis://redis:6379" + "Settings.saml_ssoe.idp_metadata_file": "config/ssoe_idp_sqa_metadata_isam.xml" + "Settings.betamocks.cache_dir": "config/vets-api-mockdata" + image: vets-api:${DOCKER_IMAGE:-latest} + restart: unless-stopped + volumes: + - "../vets-api-mockdata:/cache" + - ../.secret:/srv/vets-api/secret:cached + - ../.pki:/srv/vets-api/pki:cached + - shared-vol:/tmp + working_dir: /app + depends_on: + - clamav + - postgres + - redis + links: + - clamav + - postgres + - redis + services: + clamav: + image: clamav/clamav + restart: unless-stopped + ports: + - 3310:3310 + volumes: + - shared-vol:/vets-api redis: image: redis:6.2-alpine restart: unless-stopped + ports: + - 6379:6379 postgres: - image: mdillon/postgis:11-alpine + command: postgres -c shared_preload_libraries=pg_stat_statements -c pg_stat_statements.track=all -c max_connections=200 environment: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-password}" POSTGRES_USER: "${POSTGRES_USER:-postgres}" - volumes: - - db-data:/var/lib/postgresql/data:cached + PGDATA: /tmp + image: postgis/postgis:14-3.3-alpine ports: - - "54320:5432" - restart: unless-stopped - vets-api: - build: - context: . - target: development - args: - sidekiq_license: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" - userid: "${VETS_API_USER_ID}" - command: > - bash -c "bundle exec rake db:migrate || bundle exec rake db:setup db:migrate - && touch tmp/caching-dev.txt && foreman start -m all=1,clamd=0,freshclam=0" - image: "vets-api:${DOCKER_IMAGE:-latest}" + - 5432:5432 volumes: - - .:/srv/vets-api/src:cached - - dev_bundle:/usr/local/bundle - - ../.secret:/srv/vets-api/secret:cached - - ../.pki:/srv/vets-api/pki:cached + - ./data:/var/lib/postgresql/data:cached + web: + <<: *common + command: bash -c "bundle exec rake db:migrate || bundle exec rake db:reset db:migrate && bundle exec rails s -b 0.0.0.0" ports: - - "3000:3000" - environment: - "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" - "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}?pool=4" - "Settings.redis.app_data.url": "redis://redis:6379" - "Settings.redis.sidekiq.url": "redis://redis:6379" - "Settings.redis.rails_cache.url": "redis://redis:6379" - "Settings.binaries.clamdscan": "clamscan" # Not running a separate process within the container for clamdscan, so we use clamscan which requires no daemon - POSTGRES_HOST: "${POSTGRES_HOST:-postgres}" - POSTGRES_PORT: "${POSTGRES_PORT:-5432}" - POSTGRES_USER: "${POSTGRES_USER:-postgres}" - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-password}" - PUMA_THREADS: "${PUMA_THREADS:-4}" - depends_on: - - postgres - - redis - links: - - postgres - - redis - restart: unless-stopped + - 3000:3000 + worker: + <<: *common + command: bundle exec sidekiq -q critical,4 -q tasker,3 -q default,2 -q low,1 + volumes: - db-data: - dev_bundle: + shared-vol: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 907cf5c2d27..f532a0a2211 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -8,7 +8,7 @@ services: environment: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-password}" POSTGRES_USER: "${POSTGRES_USER:-postgres}" - vets-api: + web: build: context: . target: development @@ -22,7 +22,6 @@ services: environment: "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}" - "Settings.binaries.clamdscan": "clamscan" # Not running a separate process within the container for clamdscan, so we use clamscan which requires no daemon "Settings.redis.app_data.url": "redis://redis:6379" "Settings.redis.sidekiq.url": "redis://redis:6379" POSTGRES_HOST: "${POSTGRES_HOST:-postgres}" diff --git a/docker-compose.yml b/docker-compose.yml index bf46aa633b9..dcac6012677 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,49 +1,61 @@ -version: '3.4' +version: '3.8' + +x-app: &common + build: + args: + BUNDLE_ENTERPRISE__CONTRIBSYS__COM: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" + context: . + environment: + BUNDLE_ENTERPRISE__CONTRIBSYS__COM: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" + "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" + "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}" + "Settings.redis.app_data.url": "redis://redis:6379" + "Settings.redis.sidekiq.url": "redis://redis:6379" + "Settings.redis.rails_cache.url": "redis://redis:6379" + image: vets-api:${DOCKER_IMAGE:-latest} + volumes: + - "../vets-api-mockdata:/cache" + - .:/app:cached + - shared-vol:/tmp + working_dir: /app + depends_on: + - clamav + - postgres + - redis + links: + - clamav + - postgres + - redis + services: + clamav: + image: clamav/clamav + ports: + - 33100:3310 + volumes: + - shared-vol:/vets-api redis: image: redis:6.2-alpine ports: - - "63790:6379" + - 63790:6379 postgres: - image: mdillon/postgis:11-alpine + command: postgres -c shared_preload_libraries=pg_stat_statements -c pg_stat_statements.track=all -c max_connections=200 environment: POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-password}" POSTGRES_USER: "${POSTGRES_USER:-postgres}" - volumes: - - ./data:/var/lib/postgresql/data:cached + PGDATA: /tmp + image: postgis/postgis:14-3.3-alpine ports: - - "54320:5432" - vets-api: - build: - context: . - target: development - args: - sidekiq_license: "${BUNDLE_ENTERPRISE__CONTRIBSYS__COM}" - userid: "${VETS_API_USER_ID}" - image: "vets-api:${DOCKER_IMAGE:-latest}" + - 54320:5432 volumes: - - .:/srv/vets-api/src:cached - - "../vets-api-mockdata:/cache" - - dev_bundle:/usr/local/bundle + - ./data:/var/lib/postgresql/data:cached + web: + <<: *common ports: - - "3000:3000" - environment: - "Settings.database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_development}?pool=4" - "Settings.test_database_url": "postgis://${POSTGRES_USER:-postgres}:${POSTGRES_PASSWORD:-password}@${POSTGRES_HOST:-postgres}:${POSTGRES_PORT:-5432}/${POSTGRES_DATABASE:-vets_api_test}" - "Settings.redis.app_data.url": "redis://redis:6379" - "Settings.redis.sidekiq.url": "redis://redis:6379" - "Settings.binaries.clamdscan": "clamscan" # Not running a separate process within the container for clamdscan, so we use clamscan which requires no daemon - POSTGRES_HOST: "${POSTGRES_HOST:-postgres}" - POSTGRES_PORT: "${POSTGRES_PORT:-5432}" - POSTGRES_USER: "${POSTGRES_USER:-postgres}" - POSTGRES_PASSWORD: "${POSTGRES_PASSWORD:-password}" - depends_on: - - postgres - - redis - links: - - postgres - - redis + - 3000:3000 + worker: + <<: *common + command: bundle exec sidekiq -q critical,4 -q tasker,3 -q default,2 -q low,1 volumes: - db-data: - dev_bundle: + shared-vol: diff --git a/lib/clamav/commands/patch_scan_command.rb b/lib/clamav/commands/patch_scan_command.rb new file mode 100644 index 00000000000..0a3586b9ff0 --- /dev/null +++ b/lib/clamav/commands/patch_scan_command.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +## Monkey Patch for the clamav SCAN COMMAND +## file path needs to be vets-api/ because of shared volumes + +# clamav-client - ClamAV client +# Copyright (C) 2014 Franck Verrot + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require 'clamav/commands/command' + +module ClamAV + module Commands + class PatchScanCommand < Command + def initialize(path, path_finder = Util) + @path = path + @path_finder = path_finder + super() + end + + def call(conn) + @path_finder.path_to_files(@path).map { |file| scan_file(conn, file) } + end + + def scan_file(conn, file) + stripped_filename = file.gsub(%r{^clamav_tmp/}, '') # need to send the file + get_status_from_response(conn.send_request("SCAN /vets-api/#{stripped_filename}")) + end + end + end +end diff --git a/lib/clamav/patch_client.rb b/lib/clamav/patch_client.rb new file mode 100644 index 00000000000..ac36546e91c --- /dev/null +++ b/lib/clamav/patch_client.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +# clamav-client - ClamAV client +# Copyright (C) 2014 Franck Verrot + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +require 'clamav/connection' +require 'clamav/commands/ping_command' +require 'clamav/commands/quit_command' +require 'clamav/commands/scan_command' +require 'clamav/commands/instream_command' +require 'clamav/util' +require 'clamav/wrappers/new_line_wrapper' +require 'clamav/wrappers/null_termination_wrapper' +require_relative 'commands/patch_scan_command' + +module ClamAV + class PatchClient + def initialize(connection = default_connection) + @connection = connection + connection.establish_connection + end + + def execute(command) + command.call(@connection) + end + + def default_connection + ClamAV::Connection.new( + socket: resolve_default_socket, + wrapper: ::ClamAV::Wrappers::NewLineWrapper.new + ) + end + + def resolve_default_socket + unix_socket, tcp_host, tcp_port = ENV.values_at('CLAMD_UNIX_SOCKET', 'CLAMD_TCP_HOST', 'CLAMD_TCP_PORT') + if tcp_host && tcp_port + ::TCPSocket.new(tcp_host, tcp_port) + else + ::UNIXSocket.new(unix_socket || '/var/run/clamav/clamd.ctl') + end + end + + def ping + execute Commands::PingCommand.new + end + + def safe?(target) + return instream(target).virus_name.nil? if target.is_a?(StringIO) + + scan(target).all? { |file| file.virus_name.nil? } + end + + private + + def instream(io) + execute Commands::InstreamCommand.new(io) + end + + def scan(file_path) + execute Commands::PatchScanCommand.new(file_path) + end + end +end diff --git a/lib/common/file_helpers.rb b/lib/common/file_helpers.rb index fb49eda26cc..3fb1ebe5e75 100644 --- a/lib/common/file_helpers.rb +++ b/lib/common/file_helpers.rb @@ -22,5 +22,22 @@ def generate_temp_file(file_body, file_name = nil) file_path end + + def generate_clamav_temp_file(file_body, file_name = nil) + file_name = SecureRandom.hex if file_name.nil? + clamav_directory = Rails.root.join('clamav_tmp') + unless Dir.exist?(clamav_directory) + # Create the directory if it doesn't exist + Dir.mkdir(clamav_directory) + end + + file_path = "clamav_tmp/#{file_name}" + + File.open(file_path, 'wb') do |file| + file.write(file_body) + end + + file_path + end end end diff --git a/lib/common/virus_scan.rb b/lib/common/virus_scan.rb index e221b911faa..aefde7e31b5 100644 --- a/lib/common/virus_scan.rb +++ b/lib/common/virus_scan.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'clamav/commands/patch_scan_command' +require 'clamav/patch_client' + module Common module VirusScan module_function @@ -8,10 +11,15 @@ def scan(file_path) # `clamd` runs within service group, needs group read File.chmod(0o640, file_path) - # NOTE: If using custom_args, no other arguments can be passed to - # ClamScan::Client.scan. All other arguments will be ignored - args = ['-c', Rails.root.join('config', 'clamd.conf').to_s, file_path] - ClamScan::Client.scan(custom_args: args) + if mock_enabled? + true + else + ClamAV::PatchClient.new.safe?(file_path) # patch to call our class + end + end + + def mock_enabled? + Settings.clamav.mock end end end diff --git a/lib/shrine/plugins/validate_virus_free.rb b/lib/shrine/plugins/validate_virus_free.rb index 260ed57d1c0..9a4cf6aca19 100644 --- a/lib/shrine/plugins/validate_virus_free.rb +++ b/lib/shrine/plugins/validate_virus_free.rb @@ -9,9 +9,11 @@ module ValidateVirusFree module AttacherMethods def validate_virus_free(message: nil) Datadog::Tracing.trace('Scan Upload for Viruses') do - cached_path = get.download.path - result = Common::VirusScan.scan(cached_path) - result.safe? || add_error_msg(message || result.body) + file_to_scan = get.download + temp_file_path = Common::FileHelpers.generate_clamav_temp_file(file_to_scan) + result = Common::VirusScan.scan(temp_file_path) + File.delete(temp_file_path) + result || add_error_msg(message || "Virus Found + #{temp_file_path}") end end diff --git a/spec/lib/shrine/plugins/validate_virus_free_spec.rb b/spec/lib/shrine/plugins/validate_virus_free_spec.rb index b98f9d56503..2bb879feec6 100644 --- a/spec/lib/shrine/plugins/validate_virus_free_spec.rb +++ b/spec/lib/shrine/plugins/validate_virus_free_spec.rb @@ -29,31 +29,23 @@ def errors context 'with errors' do before do - allow(ClamScan.configuration).to receive(:client_location).and_return('found') + allow(Common::VirusScan).to receive(:scan).and_return(false) end context 'while in development' do it 'logs an error message if clamd is not running' do expect(Rails.env).to receive(:development?).and_return(true) expect(Rails.logger).to receive(:error).with(/PLEASE START CLAMD/) - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', - safe?: false, - body: 'ERROR: Could not lookup : nodename nor servname provided, or not known')) - - result = instance.validate_virus_free + result = instance.validate_virus_free(message: 'nodename nor servname provided') expect(result).to be(true) end end context 'with the default error message' do it 'adds an error if clam scan returns not safe' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: false, body: nil)) - result = instance.validate_virus_free expect(result).to be(false) - expect(instance.errors).to eq(['virus or malware detected']) + expect(instance.errors).to include(match(/Virus Found/)) end end @@ -61,9 +53,6 @@ def errors let(:message) { 'oh noes!' } it 'adds an error with a custom error message if clam scan returns not safe' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: false)) - result = instance.validate_virus_free(message:) expect(result).to be(false) expect(instance.errors).to eq(['oh noes!']) @@ -71,21 +60,18 @@ def errors end end - it 'does not add an error if clam scan returns safe' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) - - expect(instance).not_to receive(:add_error_msg) - result = instance.validate_virus_free - expect(result).to be(true) - end + context 'it returns safe' do + before do + allow(Common::VirusScan).to receive(:scan).and_return(true) + end - it 'changes group permissions of the uploaded file' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) + it 'does not add an error if clam scan returns safe' do + allow_any_instance_of(ClamAV::PatchClient).to receive(:safe?).and_return(true) - expect(File).to receive(:chmod).with(0o640, 'foo/bar.jpg').and_return(1) - instance.validate_virus_free + expect(instance).not_to receive(:add_error_msg) + result = instance.validate_virus_free + expect(result).to be(true) + end end end end diff --git a/spec/models/persistent_attachments/dependency_claim_spec.rb b/spec/models/persistent_attachments/dependency_claim_spec.rb index a4339736e80..7e9ac88134a 100644 --- a/spec/models/persistent_attachments/dependency_claim_spec.rb +++ b/spec/models/persistent_attachments/dependency_claim_spec.rb @@ -6,13 +6,16 @@ let(:file) { Rails.root.join('spec', 'fixtures', 'files', 'marriage-certificate.pdf') } let(:instance) { described_class.new(form_id: '686C-674') } + before do + allow(Common::VirusScan).to receive(:scan).and_return(true) + end + it 'sets a guid on initialize' do expect(instance.guid).to be_a(String) end it 'allows adding a file' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) + allow_any_instance_of(ClamAV::PatchClient).to receive(:safe?).and_return(true) instance.file = file.open expect(instance.valid?).to be(true) expect(instance.file.shrine_class).to be(ClaimDocumentation::Uploader) diff --git a/spec/models/persistent_attachments/lgy_claim_spec.rb b/spec/models/persistent_attachments/lgy_claim_spec.rb index b0c06a6d131..230b9db1467 100644 --- a/spec/models/persistent_attachments/lgy_claim_spec.rb +++ b/spec/models/persistent_attachments/lgy_claim_spec.rb @@ -6,13 +6,16 @@ let(:file) { Rails.root.join('spec', 'fixtures', 'files', 'marriage-certificate.pdf') } let(:instance) { described_class.new(form_id: '28-1880') } + before do + allow(Common::VirusScan).to receive(:scan).and_return(true) + end + it 'sets a guid on initialize' do expect(instance.guid).to be_a(String) end it 'allows adding a file' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) + allow_any_instance_of(ClamAV::PatchClient).to receive(:safe?).and_return(true) instance.file = file.open expect(instance.valid?).to be(true) expect(instance.file.shrine_class).to be(ClaimDocumentation::Uploader) diff --git a/spec/models/persistent_attachments/pension_burial_spec.rb b/spec/models/persistent_attachments/pension_burial_spec.rb index 86485ad393d..2dd2bdb0ec4 100644 --- a/spec/models/persistent_attachments/pension_burial_spec.rb +++ b/spec/models/persistent_attachments/pension_burial_spec.rb @@ -6,13 +6,16 @@ let(:file) { Rails.root.join('spec', 'fixtures', 'files', 'doctors-note.pdf') } let(:instance) { described_class.new(form_id: 'T-123') } + before do + allow(Common::VirusScan).to receive(:scan).and_return(true) + end + it 'sets a guid on initialize' do expect(instance.guid).to be_a(String) end it 'allows adding a file' do - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) + allow_any_instance_of(ClamAV::PatchClient).to receive(:safe?).and_return(true) instance.file = file.open expect(instance.valid?).to be(true) expect(instance.file.shrine_class).to be(ClaimDocumentation::Uploader) diff --git a/spec/requests/claim_documents_spec.rb b/spec/requests/claim_documents_spec.rb index f927db74dfa..a02810c323b 100644 --- a/spec/requests/claim_documents_spec.rb +++ b/spec/requests/claim_documents_spec.rb @@ -6,8 +6,8 @@ before do allow(Rails.logger).to receive(:info) allow(Rails.logger).to receive(:error) - allow(ClamScan::Client).to receive(:scan) - .and_return(instance_double('ClamScan::Response', safe?: true)) + allow(Common::VirusScan).to receive(:scan).and_return(true) + allow_any_instance_of(Common::VirusScan).to receive(:scan).and_return(true) end context 'with a valid file' do diff --git a/spec/simplecov_helper.rb b/spec/simplecov_helper.rb index cb82fd49798..78b15d8fbb7 100644 --- a/spec/simplecov_helper.rb +++ b/spec/simplecov_helper.rb @@ -39,7 +39,6 @@ def merge_results def self.add_filters add_filter 'app/controllers/concerns/accountable.rb' - add_filter 'config/initializers/clamscan.rb' add_filter 'lib/apps/configuration.rb' add_filter 'lib/apps/responses/response.rb' add_filter 'lib/config_helper.rb' diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 593140521e4..ac0ff59beac 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,7 +26,6 @@ add_filter 'app/controllers/concerns/accountable.rb' add_filter 'app/models/in_progress_disability_compensation_form.rb' add_filter 'app/serializers/appeal_serializer.rb' - add_filter 'config/initializers/clamscan.rb' add_filter 'lib/apps/configuration.rb' add_filter 'lib/apps/responses/response.rb' add_filter 'lib/config_helper.rb' diff --git a/spec/support/uploader_helpers.rb b/spec/support/uploader_helpers.rb index bb2cf7abd36..f7559ce71fb 100644 --- a/spec/support/uploader_helpers.rb +++ b/spec/support/uploader_helpers.rb @@ -7,14 +7,8 @@ module UploaderHelpers module ClassMethods def stub_virus_scan - let(:result) do - { - safe?: true - } - end - before do - allow(Common::VirusScan).to receive(:scan).and_return(OpenStruct.new(result)) + allow(Common::VirusScan).to receive(:scan).and_return(true) end end end diff --git a/spec/uploaders/uploader_virus_scan_spec.rb b/spec/uploaders/uploader_virus_scan_spec.rb index 6f8d0a330f8..0726bde9961 100644 --- a/spec/uploaders/uploader_virus_scan_spec.rb +++ b/spec/uploaders/uploader_virus_scan_spec.rb @@ -24,19 +24,15 @@ def store_image end context 'with a virus' do - let(:result) do - { - safe?: false, - body: 'virus found' - } - end + let(:result) { false } it 'raises an error' do + allow(Common::VirusScan).to receive(:scan).and_return(false) expect(Rails.env).to receive(:production?).and_return(true) expect(file).to receive(:delete) expect { store_image }.to raise_error( - UploaderVirusScan::VirusFoundError, 'virus found' + UploaderVirusScan::VirusFoundError ) end end