R/utils.R

Defines functions handle_response .kb_reshape date_convert

date_convert <- function(.x) {
  if (length(.x)) {
    as.POSIXct(.x, format="%Y-%m-%dT%H:%M:%OS", tz="GMT")
  } else {
    as.POSIXct(NA)
  }
}

.kb_reshape <- function(.x, path) {

  if (length(.x) == 0) return(data.frame(stringsAsFactors=FALSE))

  if (path == "/v1/topics") {

    easy_cols <- .x[, c("id", "editorId", "name", "created", "revisionDate", "disclosureDate", "document")]

    easy_cols[["created"]] <- date_convert(easy_cols[["created"]])
    easy_cols[["revisionDate"]] <- date_convert(easy_cols[["revisionDate"]])

    easy_cols$references <- .x[["metadata"]][["references"]]

    cvss_v3 <- .x[["metadata"]][["baseMetricV3"]][["cvssV3"]]

    easy_cols$cvss_v3_scope <- cvss_v3[["scope"]]
    easy_cols$cvss_v3_version <- cvss_v3[["version"]]
    easy_cols$cvss_v3_baseScore <- cvss_v3[["baseScore"]]
    easy_cols$cvss_v3_attackVector <- cvss_v3[["attackVector"]]
    easy_cols$cvss_v3_baseSeverity <- cvss_v3[["baseSeverity"]]
    easy_cols$cvss_v3_vectorString <- cvss_v3[["vectorString"]]
    easy_cols$cvss_v3_integrityImpact <- cvss_v3[["integrityImpact"]]
    easy_cols$cvss_v3_userInteraction <- cvss_v3[["userInteraction"]]
    easy_cols$cvss_v3_attackComplexity <- cvss_v3[["attackComplexity"]]
    easy_cols$cvss_v3_availabilityImpact <- cvss_v3[["availabilityImpact"]]
    easy_cols$cvss_v3_privilegesRequired <- cvss_v3[["privilegesRequired"]]
    easy_cols$cvss_v3_confidentialityImpact <- cvss_v3[["confidentialityImpact"]]
    easy_cols$cvss_v3_impact <- .x[["metadata"]][["baseMetricV3"]][["impactScore"]]
    easy_cols$cvss_v3_exploitability <- .x[["metadata"]][["baseMetricV3"]][["exploitabilityScore"]]
    easy_cols$vulnerable_versions <- .x[["metadata"]][["vulnerable-versions"]]
    easy_cols$kb_attacker_value <- .x[["score"]][["attackerValue"]]
    easy_cols$kb_exploitability <- .x[["score"]][["exploitability"]]
    easy_cols$tag_commonEnterprise <- .x$tags[["commonEnterprise"]]
    easy_cols$tag_defaultConfiguration <- .x$tags[["defaultConfiguration"]]
    easy_cols$tag_difficultToPatch <- .x$tags[["difficultToPatch"]]
    easy_cols$tag_highPrivilegeAccess <- .x$tags[["highPrivilegeAccess"]]
    easy_cols$tag_easyToDevelop <- .x$tags[["easyToDevelop"]]
    easy_cols$tag_requiresInteraction <- .x$tags[["requiresInteraction"]]
    easy_cols$tag_obscureConfiguration <- .x$tags[["obscureConfiguration"]]
    easy_cols$tag_difficultToExploit <- .x$tags[["difficultToExploit"]]
    easy_cols$tag_noUsefulData <- .x$tags[["noUsefulData"]]
    easy_cols$tag_difficultToDevelop <- .x$tags[["difficultToDevelop"]]
    easy_cols$tag_preAuth <- .x$tags[["preAuth"]]
    easy_cols$tag_postAuth <- .x$tags[["postAuth"]]

    easy_cols

    rownames(easy_cols) <- NULL

    easy_cols

  } else if (path == "/v1/topic") {

    ## List of 10
    ## $ created       : chr "2019-05-14T18:28:19.31074Z"
    ## $ disclosureDate: chr "2019-05-16T19:29:00Z"
    ## $ document      : chr "A bug in Windows Remote Desktop protocol allows unauthenticated users to run arbitrary code via a specially cra"| __truncated__
    ## $ editorId      : chr "7191a637-aa4e-4885-98a0-f4f2da285b99"
    ## $ id            : chr "131226a6-a1e9-48a1-a5d0-ac94baf8dfd2"
    ## $ metadata      :List of 16
    ## $ name          : chr "Windows Remote Desktop (RDP) Use-after-free vulnerablility, \"Bluekeep\""
    ## $ revisionDate  : chr "2020-03-03T16:18:02.56368Z"
    ## $ score         :List of 2
    ## $ tags          :List of 12

    .x[["created"]] <- date_convert(.x[["created"]])
    .x[["disclosureDate"]] <- date_convert(.x[["disclosureDate"]])
    .x[["metadata"]] <- I(list(.x[["metadata"]]))
    .x[["score"]] <- I(list(.x[["score"]]))
    .x[["tags"]] <- I(list(.x[["tags"]]))
    .x[["references"]] <- I(list(.x[["references"]]))

    if ("rapid7Analysis" %in% names(.x)) {
      if (length(.x[["rapid7Analysis"]])) {
        .x[["rapid7Analysis"]] <- paste0(.x[["rapid7Analysis"]], collapse = " ")
      } else {
        .x[["rapid7Analysis"]] <- NA_character_
      }
    }

    if ("rapid7AnalysisCreated" %in% names(.x)) {
      if (length(.x[["rapid7AnalysisCreated"]])) {
        .x[["rapid7AnalysisCreated"]] <- paste0(.x[["rapid7AnalysisCreated"]], collapse = " ")
      } else {
        .x[["rapid7AnalysisCreated"]] <- NA_character_
      }
    }

    if ("rapid7AnalysisRevisionDate" %in% names(.x)) {
      if (length(.x[["rapid7AnalysisRevisionDate"]])) {
        .x[["rapid7AnalysisRevisionDate"]] <- paste0(.x[["rapid7AnalysisRevisionDate"]], collapse = " ")
      } else {
        .x[["rapid7AnalysisRevisionDate"]] <- NA_character_
      }
    }

    .x <- as.data.frame(.x, stringsAsFactors = FALSE)

    .x

  } else if (path == "/v1/contributors") {

    .x[["created"]] <- date_convert(.x[["created"]])
    .x

  } else if (path == "/v1/assessments") {

    easy_cols <- .x[,c("id", "editorId", "topicId", "created", "revisionDate", "document", "score")]

    easy_cols[["created"]] <- date_convert(easy_cols[["created"]])
    easy_cols[["revisionDate"]] <- date_convert(easy_cols[["revisionDate"]])

    metadata <- easy_cols[["metadata"]]

    easy_cols$tags <- metadata[["tags"]]
    easy_cols$attacker_value <- metadata[["attacker-value"]]
    easy_cols$exploitability <- metadata[["exploitability"]]
    easy_cols$stability <- metadata[["stability"]]
    easy_cols$reliability <- metadata[["reliability"]]
    easy_cols$urgent_to_patch <- metadata[["urgent-to-patch"]]
    easy_cols$used_successfully <- metadata[["used-successfully"]]
    easy_cols$mitigation_strength <- metadata[["mitigation-strength"]]
    easy_cols$confidence_in_ratings <- metadata[["confidence-in-ratings"]]
    easy_cols$effort_to_develop_exploit <- metadata[["effort-to-develop-exploit"]]
    easy_cols$versions <- metadata[["versions"]]
    easy_cols$mitigation <- metadata[["mitigation"]]
    easy_cols$forever_day_versions <- metadata[["forever_day_versions"]]
    easy_cols$offensive_application <- metadata[["offensive-application"]]
    easy_cols$atacker_utility <- metadata[["atacker-utility"]]
    easy_cols$urgent_topatch <- metadata[["urgent-to patch"]]

    easy_cols

  }

}

handle_response <- function(.x, api_key = attackerkb_api_key(), came_from = NULL) {

  .pb <- progress::progress_bar$new(format = "(:spin)", total = NA)
  .pb$tick()

  path <- .x[["links"]][["self"]][["href"]]

  if (length(path) == 0) path <- sub("^kb_", "/v1/", came_from)

  ret <- .x[["data"]]

  ret <- .kb_reshape(ret, path)

  next_href <- .x[["links"]][["next"]][["href"]]

  while(length(next_href)) {

    .pb$tick()

    httr::GET(
      url = sprintf("https://api.attackerkb.com%s", next_href),
      .ATTACKERKB_UA,
      httr::add_headers(`Authorization` = sprintf("basic %s", api_key))
    ) -> res

    httr::stop_for_status(res)

    out <- httr::content(res, as = "text", encoding = "UTF-8")
    out <- jsonlite::fromJSON(out)

    next_href <- out[["links"]][["next"]][["href"]]

    ret <- data.table::rbindlist(list(ret, .kb_reshape(out[["data"]], path)), fill = TRUE)

  }

  class(ret) <- c("tbl_df", "tbl", "data.frame")
  ret

}
hrbrmstr/attackerkb documentation built on Oct. 30, 2020, 10:20 a.m.