class Pipedawg::Job::Qualys::Scan

Pipedawg::Job::Qualys::Scan class

Public Class Methods

new(name, opts = {}) click to toggle source
Calls superclass method Pipedawg::Job::Qualys::new
# File lib/pipedawg/job/qualys/scan.rb, line 8
def initialize(name, opts = {})
  opts = {
    acceptable_risk: '${QUALYS_ACCEPTABLE_IMAGE_RISK}',
    artifacts: { expire_in: '1 month', paths: ['software.json', 'vulnerabilities.json'], when: 'always' },
    config: { auths: { '$CI_REGISTRY': { username: '$CI_REGISTRY_USER', password: '$CI_REGISTRY_PASSWORD' } } },
    gateway: '${QUALYS_GATEWAY}', image: nil, password: '${QUALYS_PASSWORD}',
    scan_image: '${QUALYS_IMAGE}', scan_target_prefix: 'qualys_scan_target',
    user: '${QUALYS_USERNAME}', variables: { GIT_STRATEGY: 'clone' }
  }.merge(opts)
  super name, opts
  update
end

Public Instance Methods

update() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 21
def update # rubocop:disable Metrics/AbcSize
  require 'json'
  opts[:script] =
    debug + config + image + clean_config + token + scan_start +
    scan_complete + artifacts + severities + outputs
end

Private Instance Methods

artifacts() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 91
def artifacts
  [
    "curl -s --location --request GET \"https://#{opts[:gateway]}/csapi/v1.2/images/$image_id/software\" --header \"Authorization: Bearer $token\" | jq . > software.json", # rubocop:disable Layout/LineLength
    "curl -s --location --request GET \"https://#{opts[:gateway]}/csapi/v1.2/images/$image_id/vuln\" --header \"Authorization: Bearer $token\" | jq . > vulnerabilities.json" # rubocop:disable Layout/LineLength
  ]
end
clean_config() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 58
def clean_config
  [
    'rm -f "${CONFIG}/config.json"',
    'rmdir "${CONFIG}"'
  ]
end
config() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 44
def config
  ['export CONFIG=$(mktemp -d)', "echo #{opts[:config].to_json.inspect} > \"${CONFIG}/config.json\""]
end
debug() click to toggle source
Calls superclass method Pipedawg::Job#debug
# File lib/pipedawg/job/qualys/scan.rb, line 30
def debug # rubocop:disable Metrics/MethodLength
  if opts[:debug]
    super + [
      'echo Qualys settings:', "echo   Qualys gateway: \"#{opts[:gateway]}\"",
      "echo   Qualys username: \"#{opts[:user]}\"",
      "if [ \"#{opts[:password]}\" != '' ]; then " \
      'echo   Qualys password is not empty; else ' \
      'echo   Qualys password is not set; exit 1; fi'
    ]
  else
    []
  end
end
image() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 48
def image
  [
    "image_target=\"#{opts[:scan_target_prefix]}:$(echo #{opts[:scan_image]} | sed 's/^[^/]*\\///'| sed 's/[:/]/-/g')\"", # rubocop:disable Layout/LineLength
    "docker --config=\"${CONFIG}\" pull \"#{opts[:scan_image]}\"",
    "docker image tag \"#{opts[:scan_image]}\" \"${image_target}\"",
    "image_id=$(docker inspect --format=\"{{index .Id}}\" \"#{opts[:scan_image]}\" | cut -c8-19)",
    'echo "Image ID: ${image_id}"'
  ]
end
outputs() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 106
def outputs # rubocop:disable Metrics/MethodLength
  [
    'if [ "$severity5" = "null" ]; then ' \
    'echo "ERROR: Wrong ImageID or problem during vulnerabilities count." >&2; ' \
    'exit 1; fi',
    'if [ "$severity4" = "null" ]; then ' \
    'echo "ERROR: Wrong ImageID or problem during vulnerabilities count." >&2; ' \
    'exit 1; fi',
    'echo "Severity5: $severity5, Severity4: $severity4"',
    'risk=$((($severity5*3)+($severity4)))',
    'echo "Risk: $risk"',
    "if (($risk > \"#{opts[:acceptable_risk]}\")); then " \
    'echo "Too many vulnerabilities. Severity5: $severity5, Severity4: $severity4" >&2; ' \
    'exit 1; fi'
  ]
end
scan_complete() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 80
def scan_complete
  [
    'while true; do ' \
    "result=$(curl -s --location --request GET \"https://#{opts[:gateway]}/csapi/v1.2/images/$image_id\" --header \"Authorization: Bearer $token\" | jq -r '.scanStatus'); " + # rubocop:disable Layout/LineLength
      'echo "Waiting for scan to complete..."; ' \
      'echo "  Result: ${result}"; ' \
      'if [ "${result}" = "SUCCESS" ]; then break; fi; ' \
      'sleep 10; done; sleep 30'
  ]
end
scan_start() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 69
def scan_start
  [
    'while true; do ' \
    "result=$(curl -s -o /dev/null -w ''%{http_code}'' --location --request GET \"https://#{opts[:gateway]}/csapi/v1.2/images/$image_id\" --header \"Authorization: Bearer $token\"); " + # rubocop:disable Layout/LineLength, Style/FormatStringToken
      'echo "Waiting for scan to start..."; ' \
      'echo "  Result: ${result}"; ' \
      'if [ "${result}" = "200" ]; then break; fi; ' \
      'sleep 10; done'
  ]
end
severities() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 98
def severities
  [
    "response=$(curl -s --location --request GET \"https://#{opts[:gateway]}/csapi/v1.2/images/$image_id/vuln/count\" --header \"Authorization: Bearer $token\")", # rubocop:disable Layout/LineLength
    'severity5=$(jq -r ".severity5Count" <<< "${response}")',
    'severity4=$(jq -r ".severity4Count" <<< "${response}")'
  ]
end
token() click to toggle source
# File lib/pipedawg/job/qualys/scan.rb, line 65
def token
  ["token=$(curl -s --location --request POST \"https://#{opts[:gateway]}/auth\" --header \"Content-Type: application/x-www-form-urlencoded\" --data-urlencode \"username=#{opts[:user]}\" --data-urlencode \"password=#{opts[:password]}\" --data-urlencode \"token=true\")"] # rubocop:disable Layout/LineLength
end