type: object

description: A universal resource identifier (URI), according to [RFC 3986](https://tools.ietf.org/html/rfc3986).

type: string

format: uri

description: A version 4 (random) universally unique identifier (UUID) as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.3).

description: The uniform resource locator (URL), according to [RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.1.3), that can be used to retrieve the referenced resource.

$ref: #/definitions/uriType

description: User created name for the reference, often the title of the page.

description: An array of one or more tags that describe the resource referenced by 'url'.

type: array

$ref: #/definitions/tagExtension

description:
broken-link: The reference link is returning a 404 error, or the site is no longer online.

customer-entitlement: Similar to Privileges Required, but specific to references that require non-public/paid access for customers of the particular vendor.

exploit: Reference contains an in-depth/detailed description of steps to exploit a vulnerability OR the reference contains any legitimate Proof of Concept (PoC) code or exploit kit.

government-resource: All reference links that are from a government agency or organization should be given the Government Resource tag.

issue-tracking: The reference is a post from a bug tracking tool such as MantisBT, Bugzilla, JIRA, Github Issues, etc...

mailing-list: The reference is from a mailing list -- often specific to a product or vendor.

mitigation: The reference contains information on steps to mitigate against the vulnerability in the event a patch can't be applied or is unavailable or for EOL product situations.

not-applicable: The reference link is not applicable to the vulnerability and was likely associated by MITRE accidentally (should be used sparingly).

patch: The reference contains an update to the software that fixes the vulnerability.

permissions-required: The reference link provided is blocked by a logon page. If credentials are required to see any information this tag must be applied.

media-coverage: The reference is from a media outlet such as a newspaper, magazine, social media, or weblog. This tag is not intended to apply to any individual's personal social media account. It is strictly intended for public media entities.

product: A reference appropriate for describing a product for the purpose of CPE or SWID.

related: A reference that is for a related (but not the same) vulnerability.

release-notes: The reference is in the format of a vendor or open source project's release notes or change log.

signature: The reference contains a method to detect or prevent the presence or exploitation of the vulnerability.

technical-description: The reference contains in-depth technical information about a vulnerability and its exploitation process, typically in the form of a presentation or whitepaper.

third-party-advisory: Advisory is from an organization that is not the vulnerable product's vendor/publisher/maintainer.

vendor-advisory: Advisory is from the vendor/publisher/maintainer of the product or the parent organization.

vdb-entry: VDBs are loosely defined as sites that provide information about this vulnerability, such as advisories, with identifiers. Included VDBs are free to access, substantially public, and have broad scope and coverage (not limited to a single vendor or research organization). See: https://www.first.org/global/sigs/vrdx/vdb-catalog

enum: broken-link customer-entitlement exploit government-resource issue-tracking mailing-list mitigation not-applicable patch permissions-required media-coverage product related release-notes signature technical-description third-party-advisory vendor-advisory vdb-entry

description: A UUID for an organization participating in the CVE program. This UUID can be used to lookup the organization record in the user registry service.

$ref: #/definitions/uuidType

description: A UUID for a user participating in the CVE program. This UUID can be used to lookup the user record in the user registry service.

description: A 2-32 character name that can be used to complement an organization's UUID.

description: Date/time format based on RFC3339 and ISO ISO8601.

format: date

format: date-time

description: Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ssZZZZ'. If timezone offset is not given, GMT (0000) is assumed.

description: A single version of a product, as expressed in its own version numbering scheme.

description: The vulnerability status of a given version or range of versions of a product. The statuses 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability. The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. There can be many reasons for an 'unknown' status, including that an investigation has not been undertaken or that a vendor has not disclosed the status.

enum: affected unaffected unknown

description: Details related to the information container provider (CNA or ADP).

$ref: #/definitions/orgId

description: The container provider's organizational UUID

$ref: #/definitions/shortName

description: The container provider's organizational short name

$ref: #/definitions/timestamp

description: Timestamp to be set by the system of record at time of submission. If dateUpdated is provided to the system of record it will be replaced by the current timestamp at the time of submission. If a provider has multiple contributions, they shall be consolidated to a final single contribution before submission, or the system of record will reject the input with, Rejected – simultaneous contributions by a single provider.

description: Provides information about the set of products and services affected by this vulnerability.

required: vendor product

required: collectionURL packageName

description: Name of the organization, project, community, individual, or user that created or maintains this product or hosted service. Can be 'N/A' if none of those apply. When collectionURL and packageName are used, this field may optionally represent the user or account within the package collection associated with the package.

description: Name of the affected product.

description: URL identifying a package collection (determines meaning of packageName).

description: Name or identifier of the affected software package as used in the package collection.

description: Affected products defined by CPE. This is an array of CPE values (vulnerable and not), we use an array so that we can make multiple statements about the same version and they are separate (if we used a JSON object we'd essentially be keying on the CPE name and they would have to overlap). Also this allows things like cveDataVersion or cveDescription to be applied directly to the product entry. This also allows more complex statements such as "Product X between versions 10.2 and 10.8" to be put in a machine-readable format. As well since multiple statements can be used multiple branches of the same product can be defined here.

title: CPE Name

description: Common Platform Enumeration (CPE) Name in either 2.2 or 2.3 format

description: A list of the affected components, features, modules, sub-components, sub-products, APIs, commands, utilities, programs, or functionalities (optional)

description: Name of the affected component, feature, module, sub-component, sub-product, API, command, utility, program, or functionality (optional).

description: A list of the affected source code files (optional)

description: Name or path or location of the affected source code file in RFC3986 compliant format (optional).

description: A list of the affected source code functions, methods, subroutines, or procedures (optional).

description: Object describing program routine.

description: Name of the affected source code file, function, method, subroutine, or procedure.

title: Platforms

description: List of specific platforms if the vulnerability is only relevant in the context of these platforms (optional). Platforms may include execution environments, operating systems, virtualization technolgies, hardware models, or computing architectures. Lack of this field or an empty array implies that the other fields are applicable for all relevant platforms.

examples:
iOS
Android
Windows
macOS
x86
ARM
64 bit
Big Endian
iPad
Chromebook
Docker

description: The URL of the source code repository, for informational purposes and/or to resolve git hash version ranges.

description: The default status for versions that are not otherwise listed in the versions list. If not specified, defaultStatus defaults to 'unknown'. Versions or defaultStatus may be omitted, but not both.

$ref: #/definitions/status

description: Set of product versions or version ranges related to the vulnerability. The versions satisfy the CNA Rules [8.1.2 requirement](https://cve.mitre.org/cve/cna/rules.html#section_8-1_cve_entry_information_requirements). Versions or defaultStatus may be omitted, but not both.

description:
A single version or a range of versions, with vulnerability status.

An entry with only 'version' and 'status' indicates the status of a single version.

Otherwise, an entry describes a range; it must include the 'versionType' property, to define the version numbering semantics in use, and 'limit', to indicate the non-inclusive upper limit of the range. The object describes the status for versions V such that 'version' <= V and V < 'limit', using the <= and < semantics defined for the specific kind of 'versionType'. Status changes within the range can be specified by an optional 'changes' list.

The algorithm to decide the status specified for a version V is:

	for entry in product.versions {
		if entry.lessThan is not present and entry.lessThanOrEqual is not present and v == entry.version {
			return entry.status
		}
		if (entry.lessThan is present and entry.version <= v and v < entry.lessThan) or
		   (entry.lessThanOrEqual is present and entry.version <= v and v <= entry.lessThanOrEqual) { // <= and < defined by entry.versionType
			status = entry.status
			for change in entry.changes {
				if change.at <= v {
					status = change.status
				}
			}
			return status
		}
	}
	return product.defaultStatus

required: version status

required: version status versionType

description: The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version.

$ref: #/definitions/version

description: The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list.

description: The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.

examples: custom git maven python rpm semver

description: The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified.

description: The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3.

description: A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.

description: The start of a single status change during the range.

required: at status

description: The version at which a status change occurs.

description: The new status in the range starting at the given version.

description: List of affected products.

$ref: #/definitions/product

description: Text in a particular language with optional alternate markup or formatted representation (e.g., Markdown) or embedded media.

$ref: #/definitions/language

description: Plain text description.

title: Supporting media

description: Supporting media data for the description such as markdown, diagrams, .. (optional). Similar to RFC 2397 each media object has three main parts: media type, media data value, and an optional boolean flag to indicate if the media data is base64 encoded.

title: Media type

description: RFC2046 compliant IANA Media type for eg., text/markdown, text/html.

examples: text/markdown text/html image/png image/svg audio/mp3

type: boolean

title: Encoding

description: If true then the value field contains the media data encoded in base64. If false then the value field contains the UTF-8 media content.

description: Supporting media content, up to 16K. If base64 is true, this field stores base64 encoded data.

required: type value

required: lang value

description: A list of multi-lingual descriptions of the vulnerability. E.g., [PROBLEMTYPE] in [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] on [PLATFORMS] allows [ATTACKER] to [IMPACT] via [VECTOR]. OR [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] [ROOT CAUSE], which allows [ATTACKER] to [IMPACT] via [VECTOR].

$ref: #/definitions/description

description: This is problem type information (e.g. CWE identifier). Must contain: At least one entry, can be text, OWASP, CWE, please note that while only one is required you can use more than one (or indeed all three) as long as they are correct). (CNA requirement: [PROBLEMTYPE])

required: lang description

description: string description of problemType, or title from CWE or OWASP

description: CWE ID of the CWE that best describes this problemType entry

description: problemtype source, text, OWASP, CWE, etc

$ref: #/definitions/references

description: This is reference data in the form of URLs or file objects (uuencoded and embedded within the JSON file, exact format to be decided, e.g. we may require a compressed format so the objects require unpacking before they are "dangerous").

$ref: #/definitions/reference

description: Collection of impacts of this vulnerability

description: This is impact type information (e.g. a text description

description: CAPEC ID that best relates to this impact

description: Prose description of the impact scenario. At a minimum provide the description given by CAPEC

$ref: #/definitions/descriptions

description: collection of impact scores with attribution

description: This is impact type information (e.g. a text description, CVSSv2, CVSSv3, etc.). Must contain: At least one entry, can be text, CVSSv2, CVSSv3, others may be added

description: Name of the score format. This provides a bit future proofing. Additional properties are not prohibitied, so this will support inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV4_4, format = 'cvssV4_4', other = cvssV4_4 json object. In the future the other properties can be converted to score properties when they become part of the schema.

description: Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.

default: GENERAL

description: Description of the scenario this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.

title: JSON Schema for Common Vulnerability Scoring System version 3.1

enum: UNKNOWN REASONABLE CONFIRMED NOT_DEFINED

description: CVSS Version

required: version vectorString baseScore baseSeverity

title: JSON Schema for Common Vulnerability Scoring System version 3.0

description: CVSS Version

title: JSON Schema for Common Vulnerability Scoring System version 2.0

enum: UNCONFIRMED UNCORROBORATED CONFIRMED NOT_DEFINED

description: CVSS Version

description: a non-standard impact description, may be prose or JSON block

description: name of the non-standard impact metrics format used.

description: JSON object not covered by another metrics format

description: Configurations required for exploiting this vulnerability.

description: Workarounds and mitigations for this vulnerability.

description: Information about solutions or remediations available for this vulnerability.

description: Information about exploits of the vulnerability.

description: This is timeline information for significant events about this vulnerability or changes to CVE entry

description: Timestamp representing when the event in the timeline occurred. The timestamp format is based on RFC3339 and ISO ISO8601, with an optional timezone. yyyy-MM-ddTHH:mm:ssZZZZ - if the timezone offset is not given, GMT (0000) is assumed.

description: The language used in the description of the event. The language field is included so that CVE records can support translations. The value must be a BCP 47 language code.

description: A summary of the event.

description: Statements acknowledging specific people, organizations, or tools recognizing the work done in researching, discovering, remediating or helping with activities related to this CVE.

description: The language used when describing the credits. The language field is included so that CVE records can support translations. The value must be a BCP 47 language code.

description: UUID of the user being credited if present in the CVE User Registry (optional). This UUID can be used to lookup the user record in the user registry service.

description:
Type or role of the entity being credited (optional). finder: identifies the vulnerability.
reporter: notifies the vendor of the vulnerability to a CNA.
analyst: validates the vulnerability to ensure accuracy or severity.
coordinator: facilitates the coordinated response process.
remediation developer: prepares a code change or other remediation plans.
remediation reviewer: reviews vulnerability remediation plans or code changes for effectiveness and completeness.
remediation verifier: tests and verifies the vulnerability or its remediation.
tool: names of tools used in vulnerability discovery or identification.
sponsor: supports the vulnerability identification or remediation activities.

enum:
finder
reporter
analyst
coordinator
remediation developer
remediation reviewer
remediation verifier
tool
sponsor
other

description:
This is the source information (who discovered it, who researched it, etc.) and optionally a chain of CNA information (e.g. the originating CNA and subsequent parent CNAs who have processed it before it arrives at the MITRE root).
 Must contain: IF this is in the root level it MUST contain a CNA_chain entry, IF this source entry is NOT in the root (e.g. it is part of a vendor statement) then it must contain at least one type of data entry.

description: BCP 47 language code, language-region

description: List of taxonomy items related to the vulnerability

description: The name of the taxonomy

description: The version of taxonomy the identifiers come from.

description: List of relationships to the taxonomy for the vulnerability.  Relationships can be between the taxonomy and the CVE or two taxonomy items

description: Identifier of the item in the taxonomy.  Used as the subject of the relationship.

description: A description of the relationship

description: The target of the relationship.  Can be the CVE ID or another taxonomy identifier

description: Tags describing the CVE entry

description:
exclusively-hosted-service: All known software and/or hardware affected by this CVE Record is known to exist only in the affected hosted service. If the vulnerability affects both hosted and on-prem software and/or hardware, then the tag should not be used.

unsupported-when-assigned: Used by the assigning CNA to indicate that when a request for a CVE assignment was received, the product was already end-of-life (EOL) or a product or specific version was deemed not to be supported by the vendor. This tag should only be applied to a CVE Record when all affected products or version lines referenced in the CVE-Record are EOL.

disputed: When one party disagrees with another party's assertion that a particular issue in software is a vulnerability, a CVE Record assigned to that issue may be tagged as being 'disputed'.

description: An object containing the vulnerability information provided by a CVE Numbering Authority (CNA). There can only be one CNA container per CVE record since there can only be one assigning CNA. The CNA container must include the required information defined in the CVE Rules, which includes a product, version, problem type, prose description, and a reference.

description: The date/time this CVE ID was associated with a vulnerability by a CNA.

description: if known, the date/time the vulnerability was disclosed publicly.

description: A title, headline, or a brief phrase summarizing the of the CVE record. Eg., Buffer overflow in Example Soft.


text:
const Cve = require('../../model/cve')
const logger = require('../../middleware/logger')
const errors = require('./error')
const getConstants = require('../../constants').getConstants
const error = new errors.CveControllerError()
const booleanIsTrue = require('../../utils/utils').booleanIsTrue
const url = process.env.NODE_ENV === 'staging' ? 'https://test.cve.org/' : 'https://cve.org/'

// Helper function to create providerMetadata object
function createProviderMetadata (orgId, shortName, updateDate) {
  return { orgId: orgId, shortName: shortName, dateUpdated: updateDate }
}

// Called by GET /cve/:id
async function getCve (req, res, next) {
  try {
    const id = req.ctx.params.id
    const cveRepo = req.ctx.repositories.getCveRepository()
    const result = await cveRepo.findOneByCveId(id)
    if (!result) {
      return res.status(404).json(error.cveRecordDne())
    }

    return res.status(200).json(result.cve)
  } catch (err) {
    next(err)
  }
}

// Called by GET /cve
async function getFilteredCves (req, res, next) {
  const CONSTANTS = getConstants()
  const options = CONSTANTS.PAGINATOR_OPTIONS

  // temporary measure to allow tests to work after fixing #920
  // tests required changing the global limit to force pagination
  if (req.TEST_PAGINATOR_LIMIT) {
    CONSTANTS.PAGINATOR_OPTIONS.limit = req.TEST_PAGINATOR_LIMIT
  }

  try {
    options.page = req.ctx.query.page ? parseInt(req.ctx.query.page) : CONSTANTS.PAGINATOR_PAGE // if 'page' query parameter is not defined, set 'page' to the default page value
    const cveRepo = req.ctx.repositories.getCveRepository()
    let state = null
    let assignerShortName = null
    let assigner = null
    let cnaModified = false
    let timeModifiedGtDateObject = null
    let timeModifiedLtDateObject = null
    let adpShortName = null
    const timeModified = {
      timeStamp: [],
      dateOperator: []
    }

    // if count_only is the only parameter, return estimated count of full set of records
    if ((Object.keys(req.ctx.query).length === 1) &&
        (req.ctx.query.count_only) &&
        (booleanIsTrue(req.ctx.query.count_only))) {
      const payload = {}
      payload.totalCount = await cveRepo.estimatedDocumentCount()
      logger.info({ uuid: req.ctx.uuid, message: 'The cve records estimated count was sent to the user.' })
      return res.status(200).json(payload) // only return estimated count, not the records
    }

    Object.keys(req.ctx.query).forEach(k => {
      const key = k.toLowerCase()
      if (key === 'time_modified.lt') {
        timeModified.dateOperator.push('lt')
        timeModified.timeStamp.push(req.ctx.query['time_modified.lt'])
        timeModifiedLtDateObject = req.ctx.query['time_modified.lt']
      } else if (key === 'time_modified.gt') {
        timeModified.dateOperator.push('gt')
        timeModified.timeStamp.push(req.ctx.query['time_modified.gt'])
        timeModifiedGtDateObject = req.ctx.query['time_modified.gt']
      } else if (key === 'state') {
        state = req.ctx.query.state
      } else if (key === 'assigner_short_name') { // the key is retrieved as lowercase
        assignerShortName = req.ctx.query.assigner_short_name
      } else if (key === 'assigner') {
        assigner = req.ctx.query.assigner
      } else if (key === 'cna_modified') {
        cnaModified = req.ctx.query.cna_modified
      } else if (key === 'adp_short_name') {
        adpShortName = req.ctx.query.adp_short_name
      }
    })

    if (cnaModified && !(timeModifiedGtDateObject || timeModifiedLtDateObject)) {
      return res.status(400).json(error.badUsageOfCnaModified())
    }

    const query = {}

    if (timeModified.timeStamp.length > 0) {
      if (!cnaModified) { query['time.modified'] = {} }

      for (let i = 0; i < timeModified.timeStamp.length; i++) {
        if (timeModified.dateOperator[i] === 'lt') {
          if (cnaModified) {
            query['cve.containers.cna.providerMetadata.dateUpdated'] = {}
            // Due to this not being the mongo created date object, we need to actually check the "ISO String" version of this _NOT_ the date object that is being created in the middleware
            query['cve.containers.cna.providerMetadata.dateUpdated'].$lt = timeModifiedLtDateObject.toISOString()
          } else {
            query['time.modified'].$lt = timeModified.timeStamp[i]
          }
        } else {
          if (cnaModified) {
            query['cve.containers.cna.providerMetadata.dateUpdated'] = {}
            // Due to this not being the mongo created date object, we need to actually check the "ISO String" version of this _NOT_ the date object that is being created in the middleware
            query['cve.containers.cna.providerMetadata.dateUpdated'].$gt = timeModifiedGtDateObject.toISOString()
          } else {
            query['time.modified'].$gt = timeModified.timeStamp[i]
          }
        }
      }
    }

    if (adpShortName) {
      query['cve.containers.adp.providerMetadata.shortName'] = adpShortName
    }

    if (state) {
      query['cve.cveMetadata.state'] = state
    }

    if (assignerShortName) {
      query['cve.cveMetadata.assignerShortName'] = assignerShortName
    }

    if (assigner) {
      query['cve.cveMetadata.assignerOrgId'] = assigner
    }

    const agt = [
      {
        $match: query
      },
      // sort before project so DocDB uses the cveId index.
      // aggregatePaginate accepts sort separately from the aggregate query
      // so need to explicitly specify it here and remove it from the options
      {
        $sort: {
          'cve.cveMetadata.cveId': 1
        }
      },
      {
        $project: {
          _id: false,
          time: false
        }
      }
    ]

    delete options.sort

    // check whether user requested count_only for filtered set of records
    if ((req.ctx.query.count_only) &&
        (booleanIsTrue(req.ctx.query.count_only))) {
      const payload = {}
      payload.totalCount = await cveRepo.countDocuments(query)
      logger.info({ uuid: req.ctx.uuid, message: 'The cve records count was sent to the user.' })
      return res.status(200).json(payload) // only return count number, not the records
    }

    const pg = await cveRepo.aggregatePaginate(agt, options)
    const payload = {
      cveRecords: pg.itemsList.map(val => { return val.cve })
    }

    if (pg.itemCount >= CONSTANTS.PAGINATOR_OPTIONS.limit) {
      payload.totalCount = pg.itemCount
      payload.itemsPerPage = pg.itemsPerPage
      payload.pageCount = pg.pageCount
      payload.currentPage = pg.currentPage
      payload.prevPage = pg.prevPage
      payload.nextPage = pg.nextPage
    }
    logger.info({ uuid: req.ctx.uuid, message: 'The cve records were sent to the user.' })
    return res.status(200).json(payload)
  } catch (err) {
    next(err)
  }
}

// Called by GET /cve_cursor
// Cursor pagination implementation of getFilteredCves. Prevents data changes from affecting results between paginated calls
async function getFilteredCvesCursor (req, res, next) {
  const CONSTANTS = getConstants()
  const PAGINATED_FIELD = 'time.created'
  // temporary measure to allow tests to work after fixing #920
  // tests required changing the global limit to force pagination
  if (req.TEST_PAGINATOR_LIMIT) {
    CONSTANTS.PAGINATOR_OPTIONS.limit = req.TEST_PAGINATOR_LIMIT
  }

  try {
    const cveRepo = req.ctx.repositories.getCveRepository()
    let state = null
    let assignerShortName = null
    let assigner = null
    let cnaModified = false
    let timeModifiedGtDateObject = null
    let timeModifiedLtDateObject = null
    let adpShortName = null
    let next = null
    let previous = null
    let limit = null || CONSTANTS.PAGINATOR_OPTIONS.limit

    const timeModified = {
      timeStamp: [],
      dateOperator: []
    }

    // if count_only is the only parameter, return estimated count of full set of records
    if ((Object.keys(req.ctx.query).length === 1) &&
          (req.ctx.query.count_only) &&
          (booleanIsTrue(req.ctx.query.count_only))) {
      const payload = {}
      payload.totalCount = await cveRepo.estimatedDocumentCount()
      logger.info({ uuid: req.ctx.uuid, message: 'The cve records estimated count was sent to the user.' })
      return res.status(200).json(payload) // only return estimated count, not the records
    }

    Object.keys(req.ctx.query).forEach(k => {
      const key = k.toLowerCase()
      if (key === 'time_modified.lt') {
        timeModified.dateOperator.push('lt')
        timeModified.timeStamp.push(req.ctx.query['time_modified.lt'])
        timeModifiedLtDateObject = req.ctx.query['time_modified.lt']
      } else if (key === 'time_modified.gt') {
        timeModified.dateOperator.push('gt')
        timeModified.timeStamp.push(req.ctx.query['time_modified.gt'])
        timeModifiedGtDateObject = req.ctx.query['time_modified.gt']
      } else if (key === 'state') {
        state = req.ctx.query.state
      } else if (key === 'assigner_short_name') { // the key is retrieved as lowercase
        assignerShortName = req.ctx.query.assigner_short_name
      } else if (key === 'assigner') {
        assigner = req.ctx.query.assigner
      } else if (key === 'cna_modified') {
        cnaModified = req.ctx.query.cna_modified
      } else if (key === 'adp_short_name') {
        adpShortName = req.ctx.query.adp_short_name
      } else if (key === 'next_page') {
        next = req.ctx.query.next_page
      } else if (key === 'previous_page') {
        previous = req.ctx.query.previous_page
      } else if (key === 'limit') {
        limit = req.ctx.query.limit
      }
    })

    if (cnaModified && !(timeModifiedGtDateObject || timeModifiedLtDateObject)) {
      return res.status(400).json(error.badUsageOfCnaModified())
    }

    const query = {}

    if (timeModified.timeStamp.length > 0) {
      if (!cnaModified) { query['time.modified'] = {} }

      for (let i = 0; i < timeModified.timeStamp.length; i++) {
        if (timeModified.dateOperator[i] === 'lt') {
          if (cnaModified) {
            query['cve.containers.cna.providerMetadata.dateUpdated'] = {}
            // Due to this not being the mongo created date object, we need to actually check the "ISO String" version of this _NOT_ the date object that is being created in the middleware
            query['cve.containers.cna.providerMetadata.dateUpdated'].$lt = timeModifiedLtDateObject.toISOString()
          } else {
            query['time.modified'].$lt = timeModified.timeStamp[i]
          }
        } else {
          if (cnaModified) {
            query['cve.containers.cna.providerMetadata.dateUpdated'] = {}
            // Due to this not being the mongo created date object, we need to actually check the "ISO String" version of this _NOT_ the date object that is being created in the middleware
            query['cve.containers.cna.providerMetadata.dateUpdated'].$gt = timeModifiedGtDateObject.toISOString()
          } else {
            query['time.modified'].$gt = timeModified.timeStamp[i]
          }
        }
      }
    }

    if (adpShortName) {
      query['cve.containers.adp.providerMetadata.shortName'] = adpShortName
    }

    if (state) {
      query['cve.cveMetadata.state'] = state
    }

    if (assignerShortName) {
      query['cve.cveMetadata.assignerShortName'] = assignerShortName
    }

    if (assigner) {
      query['cve.cveMetadata.assignerOrgId'] = assigner
    }

    // check whether user requested count_only for filtered set of records
    if ((req.ctx.query.count_only) &&
          (booleanIsTrue(req.ctx.query.count_only))) {
      const payload = {}
      payload.totalCount = await cveRepo.countDocuments(query)
      logger.info({ uuid: req.ctx.uuid, message: 'The cve records count was sent to the user.' })
      return res.status(200).json(payload) // only return count number, not the records
    }

    const pg = await cveRepo.cursorPaginate(query, limit, next, previous, PAGINATED_FIELD)

    const payload = {
      cveRecords: pg.results.map(val => { return { ...val.cve } })
    }

    payload.hasNext = pg.hasNext
    payload.hasPrevious = pg.hasPrevious
    payload.next = pg.next
    payload.previous = pg.previous

    logger.info({ uuid: req.ctx.uuid, message: 'The cve records were sent to the user.' })
    return res.status(200).json(payload)
  } catch (err) {
    next(err)
  }
}

// Called by POST /cve/:id
// Creates a new CVE only if it does not exists for the specified CVE ID in the request body. If it exists, it does not
// update the CVE.
async function submitCve (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const newCve = new Cve({ cve: req.ctx.body })
    const id = req.ctx.params.id
    const cveId = newCve.cve.cveMetadata.cveId
    const state = newCve.cve.cveMetadata.state
    const cveRepo = req.ctx.repositories.getCveRepository()
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()

    // the cve id provided in the body must match the cve id provided in the URL params
    if (id !== cveId) {
      return res.status(400).json(error.cveIdMismatch())
    }

    // check that cve does not have status 'RESERVED'
    if (state === CONSTANTS.CVE_STATES.RESERVED) {
      return res.status(400).json(error.cveCreateUnsupportedState(CONSTANTS.CVE_STATES.RESERVED))
    }

    // check that cve id exists
    let result = await cveIdRepo.findOneByCveId(id)
    if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(403).json(error.cveDne())
    }

    // check that cve record does not exist
    result = await cveRepo.findOneByCveId(id)
    if (result) {
      return res.status(400).json(error.cveRecordExists())
    }

    await cveRepo.updateByCveId(cveId, newCve, { upsert: true })
    await cveIdRepo.updateByCveId(cveId, { state: state })

    const responseMessage = {
      message: cveId + ' record was successfully created.',
      created: newCve.cve
    }

    const payload = {
      action: 'create_cve_record',
      change: cveId + ' record was successfully created.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      cve: cveId
    }
    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by PUT /cve/:id
// Updates a CVE if one exists for the specified CVE ID
async function updateCve (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const newCve = new Cve({ cve: req.ctx.body })
    const cveId = req.ctx.params.id
    const cveRepo = req.ctx.repositories.getCveRepository()
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const newCveMetaData = newCve.cve.cveMetadata
    const newCveId = newCveMetaData.cveId
    const newCveState = newCveMetaData.state

    if (cveId !== newCveId) {
      return res.status(400).json(error.cveIdMismatch())
    }

    if (newCveState === CONSTANTS.CVE_STATES.RESERVED) {
      return res.status(400).json(error.cveUpdateUnsupportedState(CONSTANTS.CVE_STATES.RESERVED))
    }

    let result = await cveIdRepo.findOneByCveId(cveId)
    if (!result) {
      logger.info(cveId + ' does not exist.')
      return res.status(403).json(error.cveDne())
    }

    result = await cveRepo.findOneByCveId(cveId)
    if (!result) {
      logger.info(cveId + ' does not exist.')
      return res.status(403).json(error.cveRecordDne())
    }

    await cveRepo.updateByCveId(cveId, newCve)
    await cveIdRepo.updateByCveId(cveId, { state: newCveState })

    const responseMessage = {
      message: cveId + ' record was successfully updated.',
      updated: newCve.cve
    }

    const payload = {
      action: 'update_cve_record',
      change: cveId + ' record was successfully updated.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      cve: cveId
    }

    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by POST /cve/:id/cna
async function submitCna (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const id = req.ctx.params.id
    const cveRepo = req.ctx.repositories.getCveRepository()
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgUuid = await orgRepo.getOrgUUID(req.ctx.org)
    const userUuid = await userRepo.getUserUUID(req.ctx.user, orgUuid)

    // check that cve id exists
    let result = await cveIdRepo.findOneByCveId(id)
    if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(400).json(error.cveDne())
    }

    // check that cveId org matches user org
    const cveId = result
    const isSecretariat = await orgRepo.isSecretariat(req.ctx.org)
    if ((cveId.owning_cna !== orgUuid) && !isSecretariat) {
      return res.status(403).json(error.owningOrgDoesNotMatch())
    }

    // check that cve record does not exist
    result = await cveRepo.findOneByCveId(id)
    if (result) {
      return res.status(403).json(error.cveRecordExists())
    }

    // create full cve record here
    const owningCna = await orgRepo.findOneByUUID(cveId.owning_cna)
    const assignerShortName = owningCna.short_name
    const cnaContainer = req.ctx.body.cnaContainer
    const dateUpdated = (new Date()).toISOString()
    const additionalCveMetadataFields = {
      assignerShortName: assignerShortName,
      dateReserved: (cveId.reserved).toISOString(),
      datePublished: dateUpdated,
      dateUpdated: dateUpdated
    }

    const providerMetadata = createProviderMetadata(orgUuid, req.ctx.org, dateUpdated)
    const cveRecord = Cve.newPublishedCve(id, cveId.owning_cna, cnaContainer, additionalCveMetadataFields, providerMetadata)
    const cveModel = new Cve({ cve: cveRecord })

    result = Cve.validateCveRecord(cveModel.cve)

    if (!result.isValid) {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.invalidCnaContainerJsonSchema(result.errors))
    }

    try {
      await cveRepo.updateByCveId(id, cveModel, { upsert: true })
      // change cve id state to publish after saving CVE Record in case above call fails
      await cveIdRepo.updateByCveId(id, { state: CONSTANTS.CVE_STATES.PUBLISHED })
    } catch (err) {
      return res.status(400).json(error.unableToStoreCveRecord())
    }

    const responseMessage = {
      message: id + ' record was successfully created. This submission should appear on ' + url + ' within 15 minutes.',
      created: cveModel.cve
    }

    const payload = {
      action: 'create_cve_record_from_cna',
      change: id + ' record was successfully created.',
      req_UUID: req.ctx.uuid,
      org_UUID: orgUuid,
      user_UUID: userUuid,
      cve: id
    }
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by PUT /cve/:id/cna
async function updateCna (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const id = req.ctx.params.id
    const cveRepo = req.ctx.repositories.getCveRepository()
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgUuid = await orgRepo.getOrgUUID(req.ctx.org)
    const userUuid = await userRepo.getUserUUID(req.ctx.user, orgUuid)

    // check that cve id exists
    let result = await cveIdRepo.findOneByCveId(id)
    if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(400).json(error.cveDne())
    }

    // check that cveId org matches user org
    const cveId = result
    const isSecretariat = await orgRepo.isSecretariat(req.ctx.org)
    if ((cveId.owning_cna !== orgUuid) && !isSecretariat) {
      return res.status(403).json(error.owningOrgDoesNotMatch())
    }

    // check that cve record does exist
    result = await cveRepo.findOneByCveId(id)
    if (!result) {
      return res.status(403).json(error.cveRecordDne())
    }

    // update cve record here
    const cveRecord = result.cve
    const cnaContainer = req.ctx.body.cnaContainer
    const dateUpdated = (new Date()).toISOString()
    cveRecord.cveMetadata.dateUpdated = dateUpdated

    if (cveRecord.cveMetadata.state === CONSTANTS.CVE_STATES.REJECTED) {
      delete cveRecord.cveMetadata.dateRejected
      if (!cveRecord.cveMetadata.datePublished) {
        cveRecord.cveMetadata.datePublished = dateUpdated
      }
    }

    if (cveRecord.cveMetadata.state !== CONSTANTS.CVE_STATES.PUBLISHED) {
      cveRecord.cveMetadata.state = CONSTANTS.CVE_STATES.PUBLISHED
    }

    const providerMetadata = createProviderMetadata(orgUuid, req.ctx.org, dateUpdated)
    cnaContainer.providerMetadata = providerMetadata
    cveRecord.containers.cna = cnaContainer
    const cveModel = new Cve({ cve: cveRecord })
    result = Cve.validateCveRecord(cveModel.cve)

    if (!result.isValid) {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.invalidCnaContainerJsonSchema(result.errors))
    }

    await cveRepo.updateByCveId(id, cveModel)
    // change cve id state to publish
    if (cveId.state === CONSTANTS.CVE_STATES.REJECTED) {
      result = await cveIdRepo.updateByCveId(id, { state: CONSTANTS.CVE_STATES.PUBLISHED })
      if (!result) {
        return res.status(500).json(error.serverError())
      }
    }

    const responseMessage = {
      message: id + ' record was successfully updated. This submission should appear on ' + url + ' within 15 minutes.',
      updated: cveModel.cve
    }

    const payload = {
      action: 'update_cve_record_from_cna',
      change: id + ' record was successfully updated.',
      req_UUID: req.ctx.uuid,
      org_UUID: orgUuid,
      user_UUID: userUuid,
      cve: id
    }
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by POST /cve/:id/reject
async function rejectCVE (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const id = req.ctx.params.id
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()

    // check that cve id exists
    const cveIdObj = await cveIdRepo.findOneByCveId(id)
    if (!cveIdObj || cveIdObj.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(400).json(error.cveDne())
    }

    // check that cve record does not exist
    const cveRepo = req.ctx.repositories.getCveRepository()
    let result = await cveRepo.findOneByCveId(id)
    if (result) {
      return res.status(400).json(error.cveRecordExists())
    }

    // Both orgs below should exist since they passed validation
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const providerOrgObj = await orgRepo.findOneByShortName(req.ctx.org)
    const owningCnaObj = await orgRepo.findOneByUUID(cveIdObj.owning_cna)

    const owningCnaShortName = owningCnaObj?.short_name

    const providerMetadata = createProviderMetadata(providerOrgObj.UUID, req.ctx.org, (new Date()).toISOString())
    const rejectedCve = Cve.newRejectedCve(cveIdObj, req.ctx.body, owningCnaShortName, providerMetadata)
    const newCveObj = new Cve({ cve: rejectedCve })

    result = Cve.validateCveRecord(newCveObj.cve)
    if (!result.isValid) {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.invalidCnaContainerJsonSchema(result.errors))
    }

    // Save rejected CVE record object
    result = await cveRepo.updateByCveId(id, newCveObj, { upsert: true })
    if (!result) {
      return res.status(500).json(error.serverError())
    }

    // Update state of CVE ID
    result = await cveIdRepo.updateByCveId(id, { state: CONSTANTS.CVE_STATES.REJECTED })
    if (!result) {
      return res.status(500).json(error.serverError())
    }

    const responseMessage = {
      message: id + ' record was successfully submitted.',
      created: newCveObj.cve
    }

    const payload = {
      action: 'submit_rejected_cve_record',
      change: id + ' record was successfully submitted.',
      req_UUID: req.ctx.uuid,
      org_UUID: await orgRepo.getOrgUUID(req.ctx.org),
      cve: id
    }
    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by PUT /cve/:id/reject
async function rejectExistingCve (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const id = req.ctx.params.id
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const cveRepo = req.ctx.repositories.getCveRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const providerOrgObj = await orgRepo.findOneByShortName(req.ctx.org)

    // check that cve id exists
    const cveIdObj = await cveIdRepo.findOneByCveId(id)
    if (!cveIdObj || cveIdObj.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(400).json(error.cveDne())
    }

    // check that cve record exists
    let result = await cveRepo.findOneByCveId(id)
    if (!result) {
      return res.status(400).json(error.cveRecordDne())
    }

    const providerMetadata = createProviderMetadata(providerOrgObj.UUID, req.ctx.org, (new Date()).toISOString())

    // update CVE record to rejected
    const updatedRecord = Cve.updateCveToRejected(id, providerMetadata, result.cve, req.ctx.body)
    const updatedCve = new Cve({ cve: updatedRecord })
    result = Cve.validateCveRecord(updatedCve.cve)
    if (!result.isValid) {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.invalidCnaContainerJsonSchema(result.errors))
    }
    result = await cveRepo.updateByCveId(id, updatedCve)
    if (!result) {
      return res.status(500).json(error.unableToUpdateByCveID())
    }

    // update cveID to rejected
    result = await cveIdRepo.updateByCveId(id, { state: CONSTANTS.CVE_STATES.REJECTED })
    if (!result) {
      return res.status(500).json(error.serverError())
    }

    const responseMessage = {
      message: id + ' record was successfully submitted.',
      updated: updatedCve.cve
    }

    const payload = {
      action: 'update_rejected_cve_record',
      change: id + ' record was successfully submitted.',
      req_UUID: req.ctx.uuid,
      org_UUID: providerOrgObj.UUID,
      cve: id
    }
    const userRepo = req.ctx.repositories.getUserRepository()
    payload.user_UUID = await userRepo.getUserUUID(req.ctx.user, payload.org_UUID)
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

// Called by PUT /cve/:id/adp
async function insertAdp (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    const id = req.ctx.params.id
    const cveRepo = req.ctx.repositories.getCveRepository()
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const userRepo = req.ctx.repositories.getUserRepository()
    const orgUuid = await orgRepo.getOrgUUID(req.ctx.org)
    const userUuid = await userRepo.getUserUUID(req.ctx.user, orgUuid)

    // check that cve id exists
    let result = await cveIdRepo.findOneByCveId(id)
    if (!result || result.state === CONSTANTS.CVE_STATES.AVAILABLE) {
      return res.status(400).json(error.cveDne())
    }

    // check that cve record does exist
    result = await cveRepo.findOneByCveId(id)
    if (!result) {
      return res.status(403).json(error.cveRecordDne())
    }

    // update cve record here
    const cveRecord = result.cve

    if (cveRecord.cveMetadata.state === CONSTANTS.CVE_STATES.REJECTED) {
      return res.status(403).json(error.cveRecordRejected())
    }

    if (!Object.prototype.hasOwnProperty.call(req.ctx.body, 'adpContainer')) {
      return res.status(400).json(error.badAdpFormat())
    }

    const adpContainer = req.ctx.body.adpContainer
    const dateUpdated = (new Date()).toISOString()
    cveRecord.cveMetadata.dateUpdated = dateUpdated

    const providerMetadata = createProviderMetadata(orgUuid, req.ctx.org, dateUpdated)
    adpContainer.providerMetadata = providerMetadata
    let dupeFound = 0
    let dupeIndex = -1
    let dupeStatus = 'new'

    if (Object.prototype.hasOwnProperty.call(cveRecord.containers, 'adp')) {
      cveRecord.containers.adp.forEach(function (item, index) {
        if (orgUuid === item.providerMetadata.orgId) {
          dupeFound = 1
          dupeIndex = index
          dupeStatus = 'replacement'
        }
      })

      logger.info('Number of ADP containers already: ' + cveRecord.containers.adp.length)
    } else {
      logger.info('There were previously zero ADP containers.')
      cveRecord.containers.adp = []
    }

    if (dupeFound === 1) {
      cveRecord.containers.adp[dupeIndex] = adpContainer
    } else {
      cveRecord.containers.adp.push(adpContainer)
    }

    const cveModel = new Cve({ cve: cveRecord })
    result = Cve.validateCveRecord(cveModel.cve)
    if (!result.isValid) {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.badAdpJson(result.errors))
    }

    await cveRepo.updateByCveId(id, cveModel)

    const outcome = id + ' record had ' + dupeStatus + ' ADP container for org ' + req.ctx.org + ' successfully inserted. This submission should appear on ' + url + ' within 15 minutes.'
    const responseMessage = {
      message: outcome,
      updated: cveModel.cve
    }

    const payload = {
      action: 'update_cve_record_from_adp',
      change: outcome,
      req_UUID: req.ctx.uuid,
      org_UUID: orgUuid,
      user_UUID: userUuid,
      cve: id
    }
    logger.info(JSON.stringify(payload))
    return res.status(200).json(responseMessage)
  } catch (err) {
    next(err)
  }
}

module.exports = {
  CVE_GET_SINGLE: getCve,
  CVE_GET_FILTERED: getFilteredCves,
  CVE_GET_FILTERED_CURSOR: getFilteredCvesCursor,
  CVE_SUBMIT: submitCve,
  CVE_UPDATE_SINGLE: updateCve,
  CVE_SUBMIT_CNA: submitCna,
  CVE_UPDATE_CNA: updateCna,
  CVE_REJECT_RECORD: rejectCVE,
  CVE_REJECT_EXISTING_CVE: rejectExistingCve,
  CVE_INSERT_ADP: insertAdp
}

repo_name: CVEProject/cve-services

repo_language: JavaScript

file_name: Dockerfile

mime_type: text/plain


text:
const idrErr = require('../../utils/error')

class CveControllerError extends idrErr.IDRError {
  cveIdMismatch () { // cve
    const err = {}
    err.error = 'CVEID_MISMATCH'
    err.message = 'The cve id parameter does not match the cve id in the JSON body.'
    return err
  }

  cveRecordExists () { // cve
    const err = {}
    err.error = 'CVE_RECORD_EXISTS'
    err.message = 'The cve record for the cve id already exists.'
    return err
  }

  cveRecordDne () { // cve
    const err = {}
    err.error = 'CVE_RECORD_DNE'
    err.message = 'The cve record for the cve id does not exist.'
    return err
  }

  cveDne () { // cve
    const err = {}
    err.error = 'CVEID_DNE'
    err.message = 'The cve id does not exist.'
    return err
  }

  cveCreateUnsupportedState (state) { // cve
    const err = {}
    err.error = 'CVE_UNSUPPORTED_RECORD_STATE'
    err.message = `Cannot create cve record in state ${state}.`
    return err
  }

  cveUpdateUnsupportedState (state) { // cve
    const err = {}
    err.error = 'CVE_UNSUPPORTED_RECORD_STATE'
    err.message = `Cannot update cve record in state ${state}.`
    return err
  }

  invalidCnaContainerJsonSchema (errors) { // cve
    const err = {}
    err.error = 'INVALID_JSON_SCHEMA'
    err.message = 'CVE cnaContainer JSON schema validation FAILED.'
    err.details = {
      errors: errors
    }
    return err
  }

  owningOrgDoesNotMatch () { // cve
    const err = {}
    err.error = 'CVEID_ORG_DOES_NOT_MATCH'
    err.message = 'The cve id owning org does not match user org.'
    return err
  }

  unableToUpdateByCveID () { // cve
    const err = {}
    err.error = 'UNABLE_TO_UPDATE_BY_CVE_ID'
    err.message = 'Could not update by CVE id.'
    return err
  }

  unableToStoreCveRecord () { // cve
    const err = {}
    err.error = 'UNABLE_TO_STORE_CVE_RECORD'
    err.message = 'A problem occurred while saving the CVE Record, ensure field names in x_ objects do not start with $ or include a null byte.'
    return err
  }

  paramDne (param) { // cve
    const err = {}
    err.error = 'BAD_PARAMETER_NAME'
    err.message = `'${param}' is not a valid parameter.`
    return err
  }

  cveRecordRejected () { // cve
    const err = {}
    err.error = 'CVE_RECORD_REJECTED'
    err.message = 'The CVE Record for the CVE ID is in the REJECTED state and cannot have an ADP container.'
    return err
  }

  badAdpJson (errors) { // cve
    const err = {}
    err.error = 'BAD_ADP_JSON'
    err.message = 'The ADP data does not comply with the JSON schema.'
    err.details = {
      errors: errors
    }
    return err
  }

  badUsageOfCnaModified () {
    const err = {}
    err.error = 'BAD_USAGE_CNA_MODIFIED'
    err.message = 'cna_modified must be used with the time modified parameter'
    return err
  }

  badAdpFormat () { // cve
    const err = {}
    err.error = 'BAD_ADP_FORMAT'
    err.message = 'The ADP data does not begin with adpContainer.'
    return err
  }
}

module.exports = {
  CveControllerError
}


text:
require('dotenv').config() // This enables us to read from the .env file
const winston = require('winston')

const myFormat = winston.format.combine(
  winston.format.colorize(),
  winston.format.timestamp(),
  winston.format.align(),
  winston.format.printf(info => {
    const { timestamp, level, message, ...args } = info
    const ts = timestamp.slice(0, 19).replace('T', ' ')
    const msg = JSON.stringify(message.trim()) // messages can only be strings (not executable objects)

    return `${ts} [${level}]: ${msg} ${
      Object.keys(args).length ? JSON.stringify(args) : ''
    }`
  })
)

const logger = winston.createLogger({
  level: 'info',
  format: myFormat
})

if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'integration' || process.env.NODE_ENV === 'prod-staging' || process.env.NODE_ENV === 'production') {
  // Write all logs with level 'info' and below ('warn' and 'error') to the console if we're in dev env
  logger.add(
    new winston.transports.Console()
  )
} else if (process.env.NODE_ENV === 'test') {
  // Silence the logs if we're in test env
  logger.add(
    new winston.transports.Console({
      silent: true
    })
  )
}

module.exports = logger


text:
// Error message strings to be used in controller/index.js files

module.exports = {
  ORG_ROLES: 'Invalid role. Valid roles are CNA, SECRETARIAT',
  USER_ROLES: 'Invalid role. Valid role is ADMIN',
  ID_QUOTA: 'The id_quota does not comply with CVE id quota limitations',
  ID_STATES: 'Invalid CVE ID state. Valid states are: RESERVED, PUBLISHED, REJECTED',
  ID_MODIFY_STATES: 'Invalid CVE ID state. Valid states are: RESERVED, REJECTED',
  CVE_FILTERED_STATES: 'Invalid record state. Valid states are: PUBLISHED, REJECTED',
  COUNT_ONLY: 'Invalid count_only value. Value should be 1, true, or yes to indicate true, or 0, false, or no to indicate false',
  TIMESTAMP_FORMAT: "Bad date, or invalid timestamp format: valid format is yyyy-MM-ddTHH:mm:ss or yyyy-MM-ddTHH:mm:ssZZZZ (to use '+' in timezone offset, encode as '%2B)",
  CNA_MODIFIED: 'Invalid cna_modified value. Value should be 1, true, or yes to indicate true, or 0, false, or no to indicate false',
  FIRSTNAME_LENGTH: 'Invalid name.first. Name must be between 1 and 100 characters in length.',
  LASTNAME_LENGTH: 'Invalid name.last. Name must be between 1 and 100 characters in length.',
  MIDDLENAME_LENGTH: 'Invalid name.middle. Name must be between 1 and 100 characters in length.',
  SUFFIX_LENGTH: 'Invalid name.suffix. Name must be between 1 and 100 characters in length.'
}


text:
const getConstants = require('../constants').getConstants
const fs = require('fs')
const cveSchemaV5 = JSON.parse(fs.readFileSync('src/middleware/5.0_bundled_schema.json'))
const argon2 = require('argon2')
const logger = require('./logger')
const Ajv = require('ajv')
const addFormats = require('ajv-formats')
const ajv = new Ajv({ allErrors: true })
addFormats(ajv)
const validate = ajv.compile(cveSchemaV5)
const uuid = require('uuid')
const errors = require('./error')
const error = new errors.MiddlewareError()
const RepositoryFactory = require('../repositories/repositoryFactory')
const rateLimit = require('express-rate-limit')

function setCacheControl (req, res, next) {
  res.set('Cache-Control', 'no-store')
  next()
}

function createCtxAndReqUUID (req, res, next) {
  const CONSTANTS = getConstants()

  try {
    req.ctx = {
      authenticated: false,
      uuid: uuid.v4(),
      org: req.header(CONSTANTS.AUTH_HEADERS.ORG),
      user: req.header(CONSTANTS.AUTH_HEADERS.USER),
      key: req.header(CONSTANTS.AUTH_HEADERS.KEY),
      repositories: new RepositoryFactory()
    }

    logger.info(JSON.stringify({ uuid: req.ctx.uuid, path: req.path }))
    next()
  } catch (err) {
    next(err)
  }
}

// Sets parameter indicating whether user is authenticated
async function optionallyValidateUser (req, res, next) {
  const org = req.ctx.org
  const user = req.ctx.user
  const key = req.ctx.key
  const userRepo = req.ctx.repositories.getUserRepository()
  const orgRepo = req.ctx.repositories.getOrgRepository()
  let authenticated = true

  try {
    if (!org || !user || !key) {
      authenticated = false
    } else {
      let orgUUID = null
      let result = null

      logger.info({ uuid: req.ctx.uuid, message: 'Authenticating user: ' + user }) // userUUID may be null if user does not exist
      orgUUID = await orgRepo.getOrgUUID(org)
      if (!orgUUID) {
        authenticated = false
      } else {
        result = await userRepo.findOneByUserNameAndOrgUUID(user, orgUUID)
        if (!result || !result.active) {
          authenticated = false
        } else {
          const isPwd = await argon2.verify(result.secret, key)
          if (!isPwd) {
            authenticated = false
          }
        }
      }
    }

    req.ctx.authenticated = authenticated
    if (authenticated) {
      logger.info({ uuid: req.ctx.uuid, message: 'SUCCESSFUL user authentication for ' + user })
    }

    next()
  } catch (err) {
    next(err)
  }
}

async function validateUser (req, res, next) {
  const org = req.ctx.org
  const user = req.ctx.user
  const key = req.ctx.key
  const userRepo = req.ctx.repositories.getUserRepository()
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const CONSTANTS = getConstants()

  try {
    if (!org) {
      return res.status(400).json(error.badRequest(CONSTANTS.AUTH_HEADERS.ORG))
    }

    if (!user) {
      return res.status(400).json(error.badRequest(CONSTANTS.AUTH_HEADERS.USER))
    }

    if (!key) {
      return res.status(400).json(error.badRequest(CONSTANTS.AUTH_HEADERS.KEY))
    }

    logger.info({ uuid: req.ctx.uuid, message: 'Authenticating user: ' + user }) // userUUID may be null if user does not exist
    const orgUUID = await orgRepo.getOrgUUID(org)
    if (!orgUUID) {
      logger.info({ uuid: req.ctx.uuid, message: org + ' organization does not exist. User authentication FAILED for ' + user })
      return res.status(401).json(error.unauthorized())
    }

    const result = await userRepo.findOneByUserNameAndOrgUUID(user, orgUUID)
    if (!result) {
      logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User not found. User authentication FAILED for ' + user }))
      return res.status(401).json(error.unauthorized())
    }

    if (!result.active) {
      logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'User deactivated. Authentication failed for ' + user }))
      return res.status(401).json(error.unauthorized())
    }

    const isPwd = await argon2.verify(result.secret, key)
    if (!isPwd) {
      logger.warn(JSON.stringify({ uuid: req.ctx.uuid, message: 'Incorrect apikey. User authentication FAILED for ' + user }))
      return res.status(401).json(error.unauthorized())
    }

    logger.info({ uuid: req.ctx.uuid, message: 'SUCCESSFUL user authentication for ' + user })
    next()
  } catch (err) {
    next(err)
  }
}

// Checks that the requester belongs to an org that has the 'BULK_DOWNLOAD' role
async function onlySecretariatOrBulkDownload (req, res, next) {
  const org = req.ctx.org
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const CONSTANTS = getConstants()

  try {
    const isSec = await orgRepo.isSecretariat(org)
    const isBulkDownload = await orgRepo.isBulkDownload(org)
    if (!(isSec || isBulkDownload)) { // error message should only mention Secretariat
      logger.info({ uuid: req.ctx.uuid, message: org + ' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT })
      return res.status(403).json(error.secretariatOnly())
    }

    logger.info({
      uuid: req.ctx.uuid,
      message: 'Confirmed ' + org + ' as a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT +
        ' or as a ' + CONSTANTS.AUTH_ROLE_ENUM.BULK_DOWNLOAD
    })
    next()
  } catch (err) {
    next(err)
  }
}

// Checks that the requester belongs to an org that has the 'SECRETARIAT' role
async function onlySecretariat (req, res, next) {
  const org = req.ctx.org
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const CONSTANTS = getConstants()

  try {
    const isSec = await orgRepo.isSecretariat(org)
    if (!isSec) {
      logger.info({ uuid: req.ctx.uuid, message: org + ' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT })
      return res.status(403).json(error.secretariatOnly())
    }

    logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' as a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT })
    next()
  } catch (err) {
    next(err)
  }
}

// Checks that the requester belongs to an org that has the 'SECRETARIAT' role or is a user with the 'ADMIN' role
async function onlySecretariatOrAdmin (req, res, next) {
  const org = req.ctx.org
  const username = req.ctx.user
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const userRepo = req.ctx.repositories.getUserRepository()
  const CONSTANTS = getConstants()

  try {
    const isSec = await orgRepo.isSecretariat(org)
    const isAdmin = await userRepo.isAdmin(username, org)
    if (!isSec && !isAdmin) {
      logger.info({ uuid: req.ctx.uuid, message: 'Request denied because \'' + org + '\' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' and \'' + username + '\' is not an ' + CONSTANTS.USER_ROLE_ENUM.ADMIN + ' user.' })
      return res.status(403).json(error.notOrgAdminOrSecretariat())
    }

    logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org + ' as a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' or an ' + CONSTANTS.USER_ROLE_ENUM.ADMIN + ' user.' })
    next()
  } catch (err) {
    next(err)
  }
}

// Checks that the requester belongs to an org that has the 'CNA' role
async function onlyCnas (req, res, next) {
  const shortName = req.ctx.org
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const CONSTANTS = getConstants()

  try {
    const org = await orgRepo.findOneByShortName(shortName) // org exists
    if (org === null) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.CNA })
      return res.status(404).json(error.cnaDoesNotExist(shortName))
    } else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT)) {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' is a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' so until Root organizations are implemented this role is allowed.' })
      next()
    } else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.CNA)) { // the org is a CNA
      logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org.short_name + ' as a ' + CONSTANTS.AUTH_ROLE_ENUM.CNA })
      next()
    } else {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' is NOT a ' + CONSTANTS.AUTH_ROLE_ENUM.CNA })
      return res.status(403).json(error.cnaOnly())
    }
  } catch (err) {
    next(err)
  }
}

// Checks that the requester belongs to an org that has the 'ADP' role
async function onlyAdps (req, res, next) {
  const shortName = req.ctx.org
  const orgRepo = req.ctx.repositories.getOrgRepository()
  const CONSTANTS = getConstants()

  try {
    const org = await orgRepo.findOneByShortName(shortName) // org exists
    if (org === null) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' is NOT an ' + CONSTANTS.AUTH_ROLE_ENUM.ADP })
      return res.status(404).json(error.adpDoesNotExist(shortName))
    } else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT)) {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' is a ' + CONSTANTS.AUTH_ROLE_ENUM.SECRETARIAT + ' so until Root organizations are implemented this role is allowed.' })
      next()
    } else if (org.authority.active_roles.includes(CONSTANTS.AUTH_ROLE_ENUM.ADP)) { // the org is an ADP
      logger.info({ uuid: req.ctx.uuid, message: 'Confirmed ' + org.short_name + ' as an ' + CONSTANTS.AUTH_ROLE_ENUM.ADP })
      next()
    } else {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' is NOT an ' + CONSTANTS.AUTH_ROLE_ENUM.ADP })
      return res.status(403).json(error.adpOnly())
    }
  } catch (err) {
    next(err)
  }
}
// Checks that an org has a role or any sort
async function onlyOrgWithPartnerRole (req, res, next) {
  const shortName = req.ctx.org
  const orgRepo = req.ctx.repositories.getOrgRepository()

  try {
    const org = await orgRepo.findOneByShortName(shortName)
    if (org === null) {
      logger.info({ uuid: req.ctx.uuid, message: shortName + ' does NOT exist ' })
      return res.status(404).json(error.orgDoesNotExist(shortName))
    } else if (org.authority.active_roles.length === 1 && org.authority.active_roles[0] === 'BULK_DOWNLOAD') {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + 'only has BULK_DOWNLOAD role ' })
      return res.status(403).json(error.orgHasNoPartnerRole(shortName))
    } else if (org.authority.active_roles.length > 0) {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' has a role ' })
      next()
    } else {
      logger.info({ uuid: req.ctx.uuid, message: org.short_name + ' does NOT have a role ' })
      return res.status(403).json(error.orgHasNoPartnerRole(shortName))
    }
  } catch (err) {
    next(err)
  }
}

function validateQueryParameterNames (queryParamNames, validNames) {
  Object.keys(queryParamNames).forEach(k => {
    if (!validNames.includes(k)) {
      const filteredMessage = k.replace(/[^A-Z0-9_ -]+/gi, ' ')
      throw new Error("'" + filteredMessage.trim() + "'" + ' is not a valid parameter name.')
    }
  })
  return true
}

async function cnaMustOwnID (req, res, next) {
  try {
    const requestingOrg = req.ctx.org
    const orgRepo = req.ctx.repositories.getOrgRepository()
    const isSecretariat = await orgRepo.isSecretariat(requestingOrg)
    const requestingOrgInfo = await orgRepo.findOneByShortName(requestingOrg)
    const id = req.ctx.params.id
    const cveIdRepo = req.ctx.repositories.getCveIdRepository()

    const cveId = await cveIdRepo.findOneByCveId(id)
    if (!cveId || ((cveId.owning_cna !== requestingOrgInfo.UUID) && !isSecretariat)) {
      return res.status(403).json(error.orgDoesNotOwnId(requestingOrg, id))
    }
    next()
  } catch (err) {
    next(err)
  }
}

function validateCveJsonSchema (req, res, next) {
  const cve = req.body
  const cveVersion = cve.dataVersion
  let cveState = cve.cveMetadata
  if (cveState === undefined) {
    logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
    return res.status(400).json(error.invalidJsonSchema(['instance.cveMetadata is not defined']))
  }
  cveState = cveState.state

  logger.info({ uuid: req.ctx.uuid, message: 'Validating CVE JSON schema.' })
  let result

  if (cveVersion === '5.0') {
    if (['PUBLISHED', 'RESERVED', 'REJECTED'].includes(cveState)) {
      result = validate(cve)
    } else {
      logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
      return res.status(400).json(error.invalidJsonSchema(['instance.cveMetadata.state is not one of enum values']))
    }
  } else {
    logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
    return res.status(400).json(error.invalidJsonSchema(['instance.dataVersion is not one of enum values']))
  }

  if (result) {
    logger.info(JSON.stringify({ uuid: req.ctx.uuid, message: 'SUCCESSFUL CVE JSON schema validation.' }))
    next()
  } else {
    logger.error(JSON.stringify({ uuid: req.ctx.uuid, message: 'CVE JSON schema validation FAILED.' }))
    const temp = validate.errors
    const errors = []
    temp.forEach((error) => {
      if (error !== '') {
        errors.push(error)
      }
    })

    return res.status(400).json(error.invalidJsonSchema(errors))
  }
}

function validateJsonSyntax (err, req, res, next) {
  if (err.status && err.message) {
    if (err.message.includes('request entity too large')) {
      console.warn('Request failed validation because entity too large')
      console.info((JSON.stringify(err)))
      return res.status(413).json(error.recordTooLarge(errors))
    } else if (err.status === 400) {
      console.warn('Request failed validation because JSON syntax is incorrect')
      console.info((JSON.stringify(err)))
      let filteredMessage = err.message
      if (filteredMessage.includes('Failed to decode param')) {
        filteredMessage = filteredMessage.replace(/[^A-Z0-9_ -]+/gi, '')
      }
      return res.status(400).json(error.invalidJsonSyntax(filteredMessage))
    } else {
      console.warn('Request failed')
      console.info((JSON.stringify(err)))
      return res.status(400).json(error.genericBadRequest(err.message))
    }
  } else {
    next(err)
  }
}

function errorHandler (err, req, res, next) {
  logger.error(JSON.stringify({ error: err.stack }))
  return res.status(500).json(error.serviceNotAvailable())
}

const limiter = rateLimit({
  // over 1 second, allow a max of 1000 requests
  // can configure by setting env vars
  windowMs: 1000 * parseInt((process.env.RATE_LIMIT_WINDOW_SECONDS || 1)),
  max: parseInt(process.env.RATE_LIMIT_MAX_CONNECTIONS || 1000),
  // apply to all requests this middleware is used, so always return the same key
  keyGenerator: (req, res) => '*',
  standardHeaders: true,
  legacyHeaders: false,
  message: error.tooManyRequests()
})

/**
 * Ensures value is a flat (one-dimensional) array of any length with values
 * that are safe to cast to a string
 *
 * @param {Any} val
 * @returns
 */
function isFlatStringArray (val) {
  const errorMsg = 'Parameter must be a one-dimensional array of strings'

  if (!Array.isArray(val)) {
    throw new Error(errorMsg)
  }

  const validTypes = [
    'boolean', 'number', 'bigint', 'string'
  ]

  for (const k of val) {
    if (!validTypes.includes(typeof k)) {
      throw new Error(errorMsg)
    }
  }

  return true
}

/**
 * Recursively casts to strings and upper-cases all items in array
 *
 * @param {Array} val
 */
function toUpperCaseArray (val) {
  if (!Array.isArray(val)) {
    return val.toString().toUpperCase()
  }

  const newArr = val.map(k => {
    if (Array.isArray(k)) {
      return toUpperCaseArray(k)
    } else {
      return k.toString().toUpperCase()
    }
  })

  return newArr
}

module.exports = {
  setCacheControl,
  optionallyValidateUser,
  validateUser,
  onlySecretariat,
  onlySecretariatOrBulkDownload,
  onlySecretariatOrAdmin,
  onlyCnas,
  onlyAdps,
  onlyOrgWithPartnerRole,
  validateQueryParameterNames,
  cnaMustOwnID,
  createCtxAndReqUUID,
  validateCveJsonSchema,
  errorHandler,
  validateJsonSyntax,
  rateLimiter: limiter,
  isFlatStringArray,
  toUpperCaseArray
}


title: CVE JSON record format

description: cve-schema specifies the CVE JSON record format. This is the blueprint for a rich set of JSON data that can be submitted by CVE Numbering Authorities (CNAs) and Authorized Data Publishers (ADPs) to describe a CVE Record. Some examples of CVE Record data include CVE ID number, affected product(s), affected version(s), and public references. While those specific items are required when assigning a CVE, there are many other optional data in the schema that can be used to enrich CVE Records for community benefit. Learn more about the CVE program at [the official website](https://cve.mitre.org). This CVE JSON record format is defined using JSON Schema. Learn more about JSON Schema [here](https://json-schema.org/).

description: A universal resource identifier (URI), according to [RFC 3986](https://tools.ietf.org/html/rfc3986).

description: A version 4 (random) universally unique identifier (UUID) as defined by [RFC 4122](https://tools.ietf.org/html/rfc4122#section-4.1.3).

description: The uniform resource locator (URL), according to [RFC 3986](https://tools.ietf.org/html/rfc3986#section-1.1.3), that can be used to retrieve the referenced resource.

description: User created name for the reference, often the title of the page.

description: An array of one or more tags that describe the resource referenced by 'url'.

description:
broken-link: The reference link is returning a 404 error, or the site is no longer online.

customer-entitlement: Similar to Privileges Required, but specific to references that require non-public/paid access for customers of the particular vendor.

exploit: Reference contains an in-depth/detailed description of steps to exploit a vulnerability OR the reference contains any legitimate Proof of Concept (PoC) code or exploit kit.

government-resource: All reference links that are from a government agency or organization should be given the Government Resource tag.

issue-tracking: The reference is a post from a bug tracking tool such as MantisBT, Bugzilla, JIRA, Github Issues, etc...

mailing-list: The reference is from a mailing list -- often specific to a product or vendor.

mitigation: The reference contains information on steps to mitigate against the vulnerability in the event a patch can't be applied or is unavailable or for EOL product situations.

not-applicable: The reference link is not applicable to the vulnerability and was likely associated by MITRE accidentally (should be used sparingly).

patch: The reference contains an update to the software that fixes the vulnerability.

permissions-required: The reference link provided is blocked by a logon page. If credentials are required to see any information this tag must be applied.

media-coverage: The reference is from a media outlet such as a newspaper, magazine, social media, or weblog. This tag is not intended to apply to any individual's personal social media account. It is strictly intended for public media entities.

product: A reference appropriate for describing a product for the purpose of CPE or SWID.

related: A reference that is for a related (but not the same) vulnerability.

release-notes: The reference is in the format of a vendor or open source project's release notes or change log.

signature: The reference contains a method to detect or prevent the presence or exploitation of the vulnerability.

technical-description: The reference contains in-depth technical information about a vulnerability and its exploitation process, typically in the form of a presentation or whitepaper.

third-party-advisory: Advisory is from an organization that is not the vulnerable product's vendor/publisher/maintainer.

vendor-advisory: Advisory is from the vendor/publisher/maintainer of the product or the parent organization.

vdb-entry: VDBs are loosely defined as sites that provide information about this vulnerability, such as advisories, with identifiers. Included VDBs are free to access, substantially public, and have broad scope and coverage (not limited to a single vendor or research organization). See: https://www.first.org/global/sigs/vrdx/vdb-catalog

description: A UUID for an organization participating in the CVE program. This UUID can be used to lookup the organization record in the user registry service.

description: A UUID for a user participating in the CVE program. This UUID can be used to lookup the user record in the user registry service.

description: A 2-32 character name that can be used to complement an organization's UUID.

description: Date/time format based on RFC3339 and ISO ISO8601.

description: Date/time format based on RFC3339 and ISO ISO8601, with an optional timezone in the format 'yyyy-MM-ddTHH:mm:ssZZZZ'. If timezone offset is not given, GMT (0000) is assumed.

description: A single version of a product, as expressed in its own version numbering scheme.

description: The vulnerability status of a given version or range of versions of a product. The statuses 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability. The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. There can be many reasons for an 'unknown' status, including that an investigation has not been undertaken or that a vendor has not disclosed the status.

description: Provides information about the set of products and services affected by this vulnerability.

description: Name of the organization, project, community, individual, or user that created or maintains this product or hosted service. Can be 'N/A' if none of those apply. When collectionURL and packageName are used, this field may optionally represent the user or account within the package collection associated with the package.

description: Name of the affected product.

description: URL identifying a package collection (determines meaning of packageName).

description: Name or identifier of the affected software package as used in the package collection.

description: Affected products defined by CPE. This is an array of CPE values (vulnerable and not), we use an array so that we can make multiple statements about the same version and they are separate (if we used a JSON object we'd essentially be keying on the CPE name and they would have to overlap). Also this allows things like cveDataVersion or cveDescription to be applied directly to the product entry. This also allows more complex statements such as "Product X between versions 10.2 and 10.8" to be put in a machine-readable format. As well since multiple statements can be used multiple branches of the same product can be defined here.

title: CPE Name

description: Common Platform Enumeration (CPE) Name in either 2.2 or 2.3 format

description: A list of the affected components, features, modules, sub-components, sub-products, APIs, commands, utilities, programs, or functionalities (optional).

description: Name of the affected component, feature, module, sub-component, sub-product, API, command, utility, program, or functionality (optional).

description: A list of the affected source code files (optional).

description: Name or path or location of the affected source code file.

description: A list of the affected source code functions, methods, subroutines, or procedures (optional).

description: Object describing program routine.

description: Name of the affected source code file, function, method, subroutine, or procedure.

title: Platforms

description: List of specific platforms if the vulnerability is only relevant in the context of these platforms (optional). Platforms may include execution environments, operating systems, virtualization technolgies, hardware models, or computing architectures. Lack of this field or an empty array implies that the other fields are applicable for all relevant platforms.

description: The URL of the source code repository, for informational purposes and/or to resolve git hash version ranges.

description: The default status for versions that are not otherwise listed in the versions list. If not specified, defaultStatus defaults to 'unknown'. Versions or defaultStatus may be omitted, but not both.

description: Set of product versions or version ranges related to the vulnerability. The versions satisfy the CNA Rules [8.1.2 requirement](https://cve.mitre.org/cve/cna/rules.html#section_8-1_cve_entry_information_requirements). Versions or defaultStatus may be omitted, but not both.

description:
A single version or a range of versions, with vulnerability status.

An entry with only 'version' and 'status' indicates the status of a single version.

Otherwise, an entry describes a range; it must include the 'versionType' property, to define the version numbering semantics in use, and 'limit', to indicate the non-inclusive upper limit of the range. The object describes the status for versions V such that 'version' <= V and V < 'limit', using the <= and < semantics defined for the specific kind of 'versionType'. Status changes within the range can be specified by an optional 'changes' list.

The algorithm to decide the status specified for a version V is:

	for entry in product.versions {
		if entry.lessThan is not present and entry.lessThanOrEqual is not present and v == entry.version {
			return entry.status
		}
		if (entry.lessThan is present and entry.version <= v and v < entry.lessThan) or
		   (entry.lessThanOrEqual is present and entry.version <= v and v <= entry.lessThanOrEqual) { // <= and < defined by entry.versionType
			status = entry.status
			for change in entry.changes {
				if change.at <= v {
					status = change.status
				}
			}
			return status
		}
	}
	return product.defaultStatus

.

description: The single version being described, or the version at the start of the range. By convention, typically 0 denotes the earliest possible version.

description: The vulnerability status for the version or range of versions. For a range, the status may be refined by the 'changes' list.

description: The version numbering system used for specifying the range. This defines the exact semantics of the comparison (less-than) operation on versions, which is required to understand the range itself. 'Custom' indicates that the version type is unspecified and should be avoided whenever possible. It is included primarily for use in conversion of older data files.

description: The non-inclusive upper limit of the range. This is the least version NOT in the range. The usual version syntax is expanded to allow a pattern to end in an asterisk `(*)`, indicating an arbitrarily large number in the version ordering. For example, `{version: 1.0 lessThan: 1.*}` would describe the entire 1.X branch for most range kinds, and `{version: 2.0, lessThan: *}` describes all versions starting at 2.0, including 3.0, 5.1, and so on. Only one of lessThan and lessThanOrEqual should be specified.

description: The inclusive upper limit of the range. This is the greatest version contained in the range. Only one of lessThan and lessThanOrEqual should be specified. For example, `{version: 1.0, lessThanOrEqual: 1.3}` covers all versions from 1.0 up to and including 1.3.

description: A list of status changes that take place during the range. The array should be sorted in increasing order by the 'at' field, according to the versionType, but clients must re-sort the list themselves rather than assume it is sorted.

description: The start of a single status change during the range.

description: The version at which a status change occurs.

description: The new status in the range starting at the given version.

description: Indicates the type of information represented in the JSON instance.

description: The version of the schema being used. Used to support multiple versions of this format.

description: This is meta data about the CVE ID such as the CVE ID, who requested it, who assigned it, when it was requested, the current state (PUBLISHED, REJECTED, etc.) and so on.  These fields are controlled by the CVE Services.

required: cveId assignerOrgId state

description: The CVE identifier that this record pertains to.

$ref: #/definitions/cveId

description: The UUID for the organization to which the CVE ID was originally assigned. This UUID can be used to lookup the organization record in the user registry service.

description: The short name for the organization to which the CVE ID was originally assigned.

$ref: #/definitions/userId

description: The user that requested the CVE identifier.

description: The date/time the record was last updated.

type: integer

description: The system of record causes this to start at 1, and increment by 1 each time a submission from a data provider changes this CVE Record. The incremented value moves to the Rejected schema upon a PUBLISHED->REJECTED transition, and moves to the Published schema upon a REJECTED->PUBLISHED transition.

description: The date/time this CVE ID was reserved in the CVE automation workgroup services system. Disclaimer: This date reflects when the CVE ID was reserved, and does not necessarily indicate when this vulnerability was discovered, shared with the affected vendor, publicly disclosed, or updated in CVE.

description: The date/time the CVE Record was first published in the CVE List.

description: State of CVE - PUBLISHED, REJECTED.

description: The UUID for the organization to which the CVE ID was originally assigned.

description: The date/time the CVE ID was rejected.

description: State of CVE - PUBLISHED, REJECTED.

description: Details related to the information container provider (CNA or ADP).

description: The container provider's organizational UUID.

description: The container provider's organizational short name.

description: Timestamp to be set by the system of record at time of submission. If dateUpdated is provided to the system of record it will be replaced by the current timestamp at the time of submission.

description: An object containing the vulnerability information provided by a CVE Numbering Authority (CNA) for a published CVE ID. There can only be one CNA container per CVE record since there can only be one assigning CNA. The CNA container must include the required information defined in the CVE Rules, which includes a product, version, problem type, prose description, and a reference.

$ref: #/definitions/providerMetadata

description: If known, the date/time the vulnerability was disclosed publicly.

$ref: #/definitions/affected

$ref: #/definitions/problemTypes

$ref: #/definitions/impacts

$ref: #/definitions/metrics

$ref: #/definitions/configurations

$ref: #/definitions/workarounds

$ref: #/definitions/solutions

$ref: #/definitions/exploits

$ref: #/definitions/timeline

required: providerMetadata descriptions affected references

description: An object containing the vulnerability information provided by a CVE Numbering Authority (CNA) for a rejected CVE ID. There can only be one CNA container per CVE record since there can only be one assigning CNA.

description: Reasons for rejecting this CVE Record.

description: Contains an array of CVE IDs that this CVE ID was rejected in favor of because the this CVE ID was assigned to the vulnerabilities.

description: An object containing the vulnerability information provided by an Authorized Data Publisher (ADP). Since multiple ADPs can provide information for a CVE ID, an ADP container must indicate which ADP is the source of the information in the object.

description: A title, headline, or a brief phrase summarizing the information in an ADP container.

description: List of affected products.

description: Text in a particular language with optional alternate markup or formatted representation (e.g., Markdown) or embedded media.

description: Plain text description.

title: Supporting media

description: Supporting media data for the description such as markdown, diagrams, .. (optional). Similar to RFC 2397 each media object has three main parts: media type, media data value, and an optional boolean flag to indicate if the media data is base64 encoded.

title: Media type

description: RFC2046 compliant IANA Media type for eg., text/markdown, text/html.

title: Encoding

description: If true then the value field contains the media data encoded in base64. If false then the value field contains the UTF-8 media content.

description: Supporting media content, up to 16K. If base64 is true, this field stores base64 encoded data.

description: A description with lang set to an English language (en, en_US, en_UK, and so on).

description: A list of multi-lingual descriptions of the vulnerability. E.g., [PROBLEMTYPE] in [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] on [PLATFORMS] allows [ATTACKER] to [IMPACT] via [VECTOR]. OR [COMPONENT] in [VENDOR] [PRODUCT] [VERSION] [ROOT CAUSE], which allows [ATTACKER] to [IMPACT] via [VECTOR].

description: This is problem type information (e.g. CWE identifier). Must contain: At least one entry, can be text, OWASP, CWE, please note that while only one is required you can use more than one (or indeed all three) as long as they are correct). (CNA requirement: [PROBLEMTYPE]).

description: Text description of problemType, or title from CWE or OWASP.

description: CWE ID of the CWE that best describes this problemType entry.

description: Problemtype source, text, OWASP, CWE, etc.,

description: This is reference data in the form of URLs or file objects (uuencoded and embedded within the JSON file, exact format to be decided, e.g. we may require a compressed format so the objects require unpacking before they are "dangerous").

description: Collection of impacts of this vulnerability.

description: This is impact type information (e.g. a text description.

description: CAPEC ID that best relates to this impact.

description: Prose description of the impact scenario. At a minimum provide the description given by CAPEC.

description: Collection of impact scores with attribution.

description: This is impact type information (e.g. a text description, CVSSv2, CVSSv3, etc.). Must contain: At least one entry, can be text, CVSSv2, CVSSv3, others may be added.

description: Name of the score format. This provides a bit future proofing. Additional properties are not prohibitied, so this will support inclusion of proprietary formats. It also provides an easy future conversion mechanism when future score formats become part of the schema. example: cvssV4_4, format = 'cvssV4_4', other = cvssV4_4 json object. In the future the other properties can be converted to score properties when they become part of the schema.

description: Description of the scenarios this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.

description: Description of the scenario this metrics object applies to. If no specific scenario is given, GENERAL is used as the default and applies when no more specific metric matches.

description: CVSS Version

description: CVSS Version

description: CVSS Version

description: A non-standard impact description, may be prose or JSON block.

description: Name of the non-standard impact metrics format used.

description: JSON object not covered by another metrics format.

description: Configurations required for exploiting this vulnerability.

description: Workarounds and mitigations for this vulnerability.

description: Information about solutions or remediations available for this vulnerability.

description: Information about exploits of the vulnerability.

description: This is timeline information for significant events about this vulnerability or changes to the CVE Record.

description: Timestamp representing when the event in the timeline occurred. The timestamp format is based on RFC3339 and ISO ISO8601, with an optional timezone. yyyy-MM-ddTHH:mm:ssZZZZ - if the timezone offset is not given, GMT (0000) is assumed.

description: The language used in the description of the event. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code.

description: A summary of the event.

description: The language used when describing the credits. The language field is included so that CVE Records can support translations. The value must be a BCP 47 language code.

description: BCP 47 language code, language-region.

description: BCP 47 language code, language-region, required to be English.

description: List of taxonomy items related to the vulnerability.

description: The name of the taxonomy.

description: List of relationships to the taxonomy for the vulnerability.  Relationships can be between the taxonomy and the CVE or two taxonomy items.

description: A description of the relationship.

description: The target of the relationship.  Can be the CVE ID or another taxonomy identifier.

description: Tags provided by a CNA describing the CVE Record.

description: Tags provided by an ADP describing the CVE Record.

description: disputed: When one party disagrees with another party's assertion that a particular issue in software is a vulnerability, a CVE Record assigned to that issue may be tagged as being 'disputed'.

title: Published

description: When a CNA populates the data associated with a CVE ID as a CVE Record, the state of the CVE Record is Published.

description:
A set of structures (called containers) used to store vulnerability information related to a specific CVE ID provided by a specific organization participating in the CVE program. Each container includes information provided by a different source.

At minimum, a 'cna' container containing the vulnerability information provided by the CNA who initially assigned the CVE ID must be included.

There can only be one 'cna' container, as there can only be one assigning CNA. However, there can be multiple 'adp' containers, allowing multiple organizations participating in the CVE program to add additional information related to the vulnerability. For the most part, the 'cna' and 'adp' containers contain the same properties. The main differences are the source of the information and the 'cna' container requires the CNA include certain fields, while the 'adp' container does not.

required: dataType dataVersion cveMetadata containers

title: Rejected

description: If the CVE ID and associated CVE Record should no longer be used, the CVE Record is placed in the Rejected state. A Rejected CVE Record remains on the CVE List so that users can know when it is invalid.