text: const assert = require('assert')
  const semverSatisfies = require('semver/functions/satisfies')
  const { convertToCsvString } = require('./src/csv')
  const bitbucket = require('./src/bitbucket')
  const { getHyphenatedDate } = require('./src/date')
  const docker = require('./src/docker')
  const github = require('./src/github')
  const php = require('./src/php')
  
  const homepageOfThisLibrary = 'https://github.com/silinternational/vulnerability-scanner'
  
  /**
   * Get the security vulnerabilities for the specified version control systems.
   *
   * If a Bitbucket workspace is specified, it will be scanned.
   *
   * If a GitHub organization is specified, it will be scanned.
   *
   * If a URL is provided where we can retrieve a CSV file of the PHP versions
   * used by various Docker images, those PHP versions will be checked for EOL.
   *
   * @param {{
      bitbucket: { username: string?, appPassword: string?, workspace: string? },
      github: { token: string, organization: string? }
      versionsCsvUrl: string?
      }} config
   * @returns {Promise<[]>}
   */
  const getSecurityVulnerabilitiesForMultiple = async config => {
    const bbConfig = config.bitbucket || {}
    const ghConfig = config.github || {}
    const versionsCsvUrl = config.versionsCsvUrl || ''
    
    assert(ghConfig.token, 'A GitHub token is required to check for vulnerabilities')
    
    assert(
      bbConfig.workspace || ghConfig.organization,
      'You must specify a GitHub organization, a Bitbucket workspace, or both'
    )
    
    const allVulnerabilities = []
    
    if (bbConfig.workspace) {
      assert(bbConfig.username, 'To scan a Bitbucket workspace, please provide a username')
      assert(bbConfig.appPassword, 'To scan a Bitbucket workspace, please provide an app password')
      
      const bbVulnerabilities = await getSecurityVulnerabilitiesForBitbucketWorkspace(
        bbConfig.username,
        bbConfig.appPassword,
        ghConfig.token,
        bbConfig.workspace,
        versionsCsvUrl
      )
      allVulnerabilities.push(...bbVulnerabilities)
    }
    
    if (ghConfig.organization) {
      const ghVulnerabilities = await getSecurityVulnerabilitiesForGitHubOrg(
        ghConfig.token,
        ghConfig.organization,
        versionsCsvUrl
      )
      allVulnerabilities.push(...ghVulnerabilities)
    }
    
    return allVulnerabilities
  }
  
  /**
   * Get the security vulnerabilities for the specified Bitbucket repo. It will
   * return an array of vulnerabilities, or `undefined` if the Bitbucket repo's
   * dependencies could not be determined.
   *
   * NOTE: The data returned about each vulnerability will include a `repo` field
   * with given repo name.
   *
   * NOTE 2: The GitHub token is needed for calls to check for published
   * security vulnerabilities.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} repo - A Bitbucket repo (formatted as "owner/library")
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<undefined | {repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getSecurityVulnerabilitiesForBitbucketRepo = async (
    bitbucketUsername,
    bitbucketAppPassword,
    githubToken,
    repo,
    versionsCsvUrl = ''
  ) => {
    console.log(`Looking for vulnerabilities in Bitbucket ${repo}`)
    let allVulnerabilities
    
    const javaScriptDependencies = await bitbucket.getJavaScriptDependenciesOfRepo(
      bitbucketUsername,
      bitbucketAppPassword,
      repo
    )
    if (javaScriptDependencies === undefined) {
      console.log('(No JavaScript dependencies found)')
    } else {
      const javaScriptVulnerabilities = await getSecurityVulnerabilitiesForJavaScriptDependencies(
        githubToken,
        javaScriptDependencies
      )
      const javaScriptVulnerabilitiesWithRepo = javaScriptVulnerabilities.map(vulnerability => addRepoName(repo, vulnerability))
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...javaScriptVulnerabilitiesWithRepo)
    }
    
    const phpDependencies = await bitbucket.getPhpDependenciesOfRepo(
      bitbucketUsername,
      bitbucketAppPassword,
      repo
    )
    if (phpDependencies === undefined) {
      console.log('(No PHP dependencies found)')
    } else {
      const phpVulnerabilities = await getSecurityVulnerabilitiesForPhpDependencies(
        githubToken,
        phpDependencies
      )
      const phpVulnerabilitiesWithRepo = phpVulnerabilities.map(vulnerability => addRepoName(repo, vulnerability))
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...phpVulnerabilitiesWithRepo)
    }
    
    if (versionsCsvUrl) {
      const phpVersionVulnerabilities = await getPhpVersionVulnerabilitiesOfBitbucketRepo(
        bitbucketUsername,
        bitbucketAppPassword,
        repo,
        versionsCsvUrl
      )
      const phpVersionVulnerabilitiesWithRepo = phpVersionVulnerabilities.map(
        vulnerability => addRepoName(repo, vulnerability)
      )
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...phpVersionVulnerabilitiesWithRepo)
    }
    
    return allVulnerabilities
  }
  
  /**
   * Get a list of security vulnerabilities affecting the specified Bitbucket
   * repos.
   *
   * Only repos whose dependencies could be determined will be included.
   *
   * NOTE: The GitHub token is needed for calls to check for published security
   * vulnerabilities.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string} githubToken - A GitHub personal access token
   * @param {string[]} repos - The list of Bitbucket repos (e.g. ["owner/library"])
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<undefined | {repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
  */
  const getSecurityVulnerabilitiesForBitbucketRepos = async (
    bitbucketUsername,
    bitbucketAppPassword,
    githubToken,
    repos,
    versionsCsvUrl = ''
  ) => {
    const results = []
    for (const repo of repos) {
      const vulnerabilities = await getSecurityVulnerabilitiesForBitbucketRepo(
        bitbucketUsername,
        bitbucketAppPassword,
        githubToken,
        repo,
        versionsCsvUrl
      )
      if (Array.isArray(vulnerabilities)) {
        results.push(...vulnerabilities)
      }
    }
    return results
  }
  
  /**
   * Get the list of security vulnerabilities affecting a given Bitbucket
   * workspace's repos.
   *
   * NOTE: The GitHub token is needed for calls to check for published security
   * vulnerabilities.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} workspace - A Bitbucket workspace (aka organization)
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getSecurityVulnerabilitiesForBitbucketWorkspace = async (
    bitbucketUsername,
    bitbucketAppPassword,
    githubToken,
    workspace,
    versionsCsvUrl = ''
  ) => {
    console.log(`Scanning for vulnerabilities in Bitbucket workspace ${workspace}`)
    const repos = await bitbucket.listRepos(
      bitbucketUsername,
      bitbucketAppPassword,
      workspace
    )
    return getSecurityVulnerabilitiesForBitbucketRepos(
      bitbucketUsername,
      bitbucketAppPassword,
      githubToken,
      repos,
      versionsCsvUrl
    )
  }
  
  const getSecurityVulnerabilitiesForJavaScriptDependency = async (githubToken, dependency) => {
    const vulnerabilities = await github.getSecurityVulnerabilitiesForPackageUsingCache(
      githubToken,
      dependency.name,
      'NPM'
    )
    return vulnerabilities.filter(vulnerability => versionSatisfiesRange(
      dependency.version,
      vulnerability.vulnerableVersionRange
    ))
  }
  
  const getSecurityVulnerabilitiesForPhpDependency = async (githubToken, dependency) => {
    const vulnerabilities = await github.getSecurityVulnerabilitiesForPackageUsingCache(
      githubToken,
      dependency.name,
      'COMPOSER'
    )
    return vulnerabilities.filter(vulnerability => versionSatisfiesRange(
      dependency.version,
      vulnerability.vulnerableVersionRange
    ))
  }
  
  const versionSatisfiesRange = (version, gitHubVulnerableVersionRange) => {
    /*
     * NOTE: GitHub's `vulnerableVersionRange` uses a comma to separate the two
     * version constraints of a range:
     * https://docs.github.com/en/free-pro-team@latest/graphql/reference/objects#securityvulnerability
     * 
     * However, npm's semver doesn't know what to do with that comma. Removing it
     * seems to result in a version range that npm's semver can understand.
     */
    const npmSemverRange = gitHubVulnerableVersionRange.replace(',', '')
    return semverSatisfies(version, npmSemverRange)
  }
  
  /**
   * Get the list of security vulnerabilities affecting a given GitHub organization's
   * repositories.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} org - A GitHub organization's name
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getSecurityVulnerabilitiesForGitHubOrg = async (
    githubToken,
    org,
    versionsCsvUrl = ''
  ) => {
    console.log(`Scanning for vulnerabilities in GitHub org. ${org}`)
    const repos = await github.listRepos(githubToken, org)
    return getSecurityVulnerabilitiesForGitHubRepos(githubToken, repos, versionsCsvUrl)
  }
  
  /**
   * Get a list of security vulnerabilities affecting the specified GitHub repos.
   *
   * Only repos whose dependencies could be determined will be included.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string[]} repos - A list of repo names
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getSecurityVulnerabilitiesForGitHubRepos = async (
    githubToken,
    repos,
    versionsCsvUrl = ''
  ) => {
    const results = []
    for (const repo of repos) {
      const vulnerabilities = await getSecurityVulnerabilitiesForGitHubRepo(
        githubToken,
        repo,
        versionsCsvUrl
      )
      if (Array.isArray(vulnerabilities)) {
        results.push(...vulnerabilities)
      }
    }
    return results
  }
  
  /**
   * Get the security vulnerabilities for the specified GitHub repo. It will
   * return an array of vulnerabilities, or `undefined` if the GitHub repo's
   * dependencies could not be determined.
   *
   * NOTE: The data returned about each vulnerability will include a `repo` field
   * with given repo name.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} repo - A GitHub repo (formatted as "owner/library")
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<undefined | {repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getSecurityVulnerabilitiesForGitHubRepo = async (
    githubToken,
    repo,
    versionsCsvUrl = ''
  ) => {
    console.log(`Looking for vulnerabilities in GitHub ${repo}`)
    let allVulnerabilities
    
    const javaScriptDependencies = await github.getJavaScriptDependenciesOfRepo(githubToken, repo)
    if (javaScriptDependencies === undefined) {
      console.log('(No JavaScript dependencies found)')
    } else {
      const javaScriptVulnerabilities = await getSecurityVulnerabilitiesForJavaScriptDependencies(
        githubToken,
        javaScriptDependencies
      )
      const javaScriptVulnerabilitiesWithRepo = javaScriptVulnerabilities.map(vulnerability => addRepoName(repo, vulnerability))
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...javaScriptVulnerabilitiesWithRepo)
    }
    
    const phpDependencies = await github.getPhpDependenciesOfRepo(githubToken, repo)
    if (phpDependencies === undefined) {
      console.log('(No PHP dependencies found)')
    } else {
      const phpVulnerabilities = await getSecurityVulnerabilitiesForPhpDependencies(
        githubToken,
        phpDependencies
      )
      const phpVulnerabilitiesWithRepo = phpVulnerabilities.map(vulnerability => addRepoName(repo, vulnerability))
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...phpVulnerabilitiesWithRepo)
    }
    
    if (versionsCsvUrl) {
      const phpVersionVulnerabilities = await getPhpVersionVulnerabilitiesOfGitHubRepo(
        githubToken,
        repo,
        versionsCsvUrl
      )
      const phpVersionVulnerabilitiesWithRepo = phpVersionVulnerabilities.map(
        vulnerability => addRepoName(repo, vulnerability)
      )
      allVulnerabilities = allVulnerabilities || []
      allVulnerabilities.push(...phpVersionVulnerabilitiesWithRepo)
    }
    
    return allVulnerabilities
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * Bitbucket repo.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string} repo - A Bitbucket repo (formatted as "owner/library")
   * @param {string} versionsCsvUrl -- The URL where we can retrieve a CSV file of
   *     the PHP versions used by various Docker images.
   * @returns {Promise<{detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfBitbucketRepo = async (
    bitbucketUsername,
    bitbucketAppPassword,
    repo,
    versionsCsvUrl
  ) => {
    console.log(`Checking PHP version(s) used in Bitbucket ${repo}`)
    
    const dockerImages = await bitbucket.getDockerBaseImagesOfRepo(
      bitbucketUsername,
      bitbucketAppPassword,
      repo
    )
    if (dockerImages.length < 1) {
      console.log(`(No Docker images specified in ${repo})`)
      return []
    }
    const vulnerabilities = await Promise.all(
      dockerImages.map(dockerImage => getPhpVersionEolVulnerability(dockerImage, versionsCsvUrl))
    )
    return vulnerabilities.filter(vulnerability => vulnerability !== undefined)
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * Bitbucket repos.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string[]} repos - The list of Bitbucket repos (e.g. ["owner/library"])
   * @param {string} versionsCsvUrl - (Optional:) The URL where we can retrieve a
   *     CSV file of the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfBitbucketRepos = async (
    bitbucketUsername,
    bitbucketAppPassword,
    repos,
    versionsCsvUrl = ''
  ) => {
    const results = []
    for (const repo of repos) {
      const vulnerabilities = await getPhpVersionVulnerabilitiesOfBitbucketRepo(
        bitbucketUsername,
        bitbucketAppPassword,
        repo,
        versionsCsvUrl
      )
      const vulnerabilitiesWithRepo = vulnerabilities.map(
        vulnerability => addRepoName(repo, vulnerability)
      )
      results.push(...vulnerabilitiesWithRepo)
    }
    return results
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * Bitbucket workspace's repos.
   *
   * @param {string} bitbucketUsername - A Bitbucket username
   * @param {string} bitbucketAppPassword - A Bitbucket app password
   * @param {string} workspace - A Bitbucket workspace (aka organization)
   * @param {string} versionsCsvUrl - The URL where we can retrieve a CSV file of
   *     the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfBitbucketWorkspace = async (
    bitbucketUsername,
    bitbucketAppPassword,
    workspace,
    versionsCsvUrl
  ) => {
    console.log(`Checking PHP version(s) used in Bitbucket workspace ${workspace}`)
    const repos = await bitbucket.listRepos(
      bitbucketUsername,
      bitbucketAppPassword,
      workspace
    )
    return getPhpVersionVulnerabilitiesOfBitbucketRepos(
      bitbucketUsername,
      bitbucketAppPassword,
      repos,
      versionsCsvUrl
    )
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * GitHub organization's repos.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} org - A GitHub organization's name
   * @param {string} versionsCsvUrl - The URL where we can retrieve a CSV file of
   *     the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfGitHubOrg = async (
    githubToken,
    org,
    versionsCsvUrl
  ) => {
    console.log(`Checking PHP version(s) used in GitHub org. ${org}`)
    const repos = await github.listRepos(githubToken, org)
    return getPhpVersionVulnerabilitiesOfGitHubRepos(githubToken, repos, versionsCsvUrl)
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * GitHub repo.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string} repo - A GitHub repo (formatted as "owner/library")
   * @param {string} versionsCsvUrl -- The URL where we can retrieve a CSV file of
   *     the PHP versions used by various Docker images.
   * @returns {Promise<{detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfGitHubRepo = async (githubToken, repo, versionsCsvUrl) => {
    console.log(`Checking PHP version(s) used in GitHub ${repo}`)
    
    const dockerImages = await github.getDockerBaseImagesOfRepo(githubToken, repo)
    if (dockerImages.length < 1) {
      console.log(`(No Docker images specified in ${repo})`)
      return []
    }
    const vulnerabilities = await Promise.all(
      dockerImages.map(dockerImage => getPhpVersionEolVulnerability(dockerImage, versionsCsvUrl))
    )
    return vulnerabilities.filter(vulnerability => vulnerability !== undefined)
  }
  
  /**
   * Report any use of an End-Of-Life'd (EOL'd) PHP version in the specified
   * GitHub repos.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {string[]} repos - A list of repo names
   * @param {string} versionsCsvUrl -- The URL where we can retrieve a CSV file of
   *     the PHP versions used by various Docker images.
   * @returns {Promise<{repo, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, severity, summary, vulnerableVersionRange}[]>}
   */
  const getPhpVersionVulnerabilitiesOfGitHubRepos = async (
    githubToken,
    repos,
    versionsCsvUrl
  ) => {
    const results = []
    for (const repo of repos) {
      const vulnerabilities = await getPhpVersionVulnerabilitiesOfGitHubRepo(
        githubToken,
        repo,
        versionsCsvUrl
      )
      const vulnerabilitiesWithRepo = vulnerabilities.map(
        vulnerability => addRepoName(repo, vulnerability)
      )
      results.push(...vulnerabilitiesWithRepo)
    }
    return results
  }
  
  /**
   * Get any vulnerability related to the specified Docker image using a PHP
   * version that has reached End-Of-Life (EOL).
   *
   * @param dockerImage
   * @param versionsCsvUrl
   * @returns {Promise<undefined | {severity, summary, detailsUrl, ecosystem, identifiers, inspectedOn, packageName, vulnerableVersionRange}>}
   */
  const getPhpVersionEolVulnerability = async (dockerImage, versionsCsvUrl) => {
    const currentDate = getHyphenatedDate()
    const phpVersion = await docker.getPhpVersionOfImageUsingCache(dockerImage, versionsCsvUrl)
    if (phpVersion === 'NONE') {
      console.log(`(PHP is not used in ${dockerImage})`)
      return undefined
    } else if (phpVersion === 'UNKNOWN') {
      const unknownDockerImageWarning = `No PHP version data found in spreadsheet for ${dockerImage}`
      console.warn(`WARNING: ${unknownDockerImageWarning}`)
      return {
        detailsUrl: homepageOfThisLibrary + '#Missing-Docker-image-values',
        ecosystem: null,
        identifiers: [],
        inspectedOn: currentDate,
        packageName: 'PHP',
        severity: 'LOW',
        summary: unknownDockerImageWarning,
        vulnerableVersionRange: null
      }
    }
    
    const isPhpVersionEOL = php.isVersionEOL(phpVersion, currentDate)
    if (isPhpVersionEOL === undefined) {
      const unknownPhpVersionWarning = `Unknown PHP version: ${phpVersion}`
      console.warn(`WARNING: ${unknownPhpVersionWarning}`)
      return {
        detailsUrl: homepageOfThisLibrary + '#Unknown-PHP-versions',
        ecosystem: null,
        identifiers: [],
        inspectedOn: currentDate,
        packageName: 'PHP',
        severity: 'LOW',
        summary: unknownPhpVersionWarning,
        vulnerableVersionRange: null
      }
    } else if (isPhpVersionEOL === true) {
      const eolPhpVersionWarning = `PHP ${phpVersion} has reached End-Of-Life`
      console.log(eolPhpVersionWarning)
      return {
        detailsUrl: 'https://php.net/eol',
        ecosystem: null,
        identifiers: [],
        inspectedOn: currentDate,
        packageName: 'PHP',
        severity: 'HIGH',
        summary: eolPhpVersionWarning,
        vulnerableVersionRange: phpVersion
      }
    }
    
    return undefined
  }
  
  const addRepoName = (repo, vulnerability) => {
    // Return a new object instead of modifying the given object (which caused problems).
    return Object.assign({repo}, vulnerability)
  }
  
  /**
   * Get any published security vulnerabilities in the given list of JavaScript
   * dependencies.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {{name: string, version: string}[]} dependencies
   * @returns {Promise<Object[]>}
   */
  const getSecurityVulnerabilitiesForJavaScriptDependencies = async (githubToken, dependencies) => {
    const results = []
    for (const dependency of dependencies) {
      console.log(`Checking ${dependency.name} [${dependency.version}]`)
      
      const vulnerabilities = await getSecurityVulnerabilitiesForJavaScriptDependency(
        githubToken,
        dependency
      )
      
      for (const vulnerability of vulnerabilities) {
        console.log(
          `! Found vulnerability: ${vulnerability.packageName} ${vulnerability.vulnerableVersionRange}`
        )
        results.push(vulnerability)
      }
    }
    
    return results
  }
  
  /**
   * Get any published security vulnerabilities in the given list of PHP
   * dependencies.
   *
   * @param {string} githubToken - A GitHub personal access token
   * @param {{name: string, version: string}[]} dependencies
   * @returns {Promise<Object[]>}
   */
  const getSecurityVulnerabilitiesForPhpDependencies = async (githubToken, dependencies) => {
    const results = []
    for (const dependency of dependencies) {
      console.log(`Checking ${dependency.name} [${dependency.version}]`)
      
      const vulnerabilities = await getSecurityVulnerabilitiesForPhpDependency(
        githubToken,
        dependency
      )
      
      for (const vulnerability of vulnerabilities) {
        console.log(
          `! Found vulnerability: ${vulnerability.packageName} ${vulnerability.vulnerableVersionRange}`
        )
        results.push(vulnerability)
      }
    }
    
    return results
  }
  
  module.exports = {
    convertToCsvString,
    getPhpVersionVulnerabilitiesOfBitbucketRepo,
    getPhpVersionVulnerabilitiesOfBitbucketWorkspace,
    getPhpVersionVulnerabilitiesOfGitHubOrg,
    getPhpVersionVulnerabilitiesOfGitHubRepo,
    getSecurityVulnerabilitiesForBitbucketRepo,
    getSecurityVulnerabilitiesForBitbucketWorkspace,
    getSecurityVulnerabilitiesForGitHubOrg,
    getSecurityVulnerabilitiesForGitHubRepo,
    getSecurityVulnerabilitiesForMultiple,
  }
  