boB Rudis
5 years ago
8 changed files with 151 additions and 42 deletions
@ -0,0 +1,71 @@ |
|||||
|
#' Generate an ATT&CK heatmap |
||||
|
#' |
||||
|
#' @param xdf a data frame with `tactic`, `technique` and `value` columns. |
||||
|
#' If no `value` column exists, then the function will assume you |
||||
|
#' have passed in individual events and will perform a "count" |
||||
|
#' summarization before generating the heatmap. |
||||
|
#' @param input,output,matrix if both are not `NULL` then they should be |
||||
|
#' what [fct_tactic()] takes as parameters. Otherwise, the function |
||||
|
#' will assume that the `tactic` column is already an ordered factor. |
||||
|
#' @param tile_col,tile_size color/size for the tile borders; |
||||
|
#' defaults to "`white`" and `0.5`, respectively. |
||||
|
#' @param dark_lab,light_lab text colors for when they appear on top of a |
||||
|
#' dark or light tile |
||||
|
#' @param dark_value_threshold since you can supply your own fill scale |
||||
|
#' and may use a transformation (e.g. "`log10`") when doing so, you |
||||
|
#' can specify the cutoff value for when to use `dark_lab` vs `light_lab`. |
||||
|
#' If `NULL` then half of `max(value)` will be used. |
||||
|
#' @param ... passed on to the internal call to [ggplot2::geom_text()] |
||||
|
#' @return a ggplot2 plot object which you can add a fill scale to as well |
||||
|
#' as themeing. |
||||
|
#' @export |
||||
|
attck_map <- function(xdf, input = NULL, output = NULL, matrix = NULL, |
||||
|
tile_col = "white", tile_size = 0.5, |
||||
|
dark_lab = "white", light_lab = "black", |
||||
|
dark_value_threshold = NULL, ...) { |
||||
|
|
||||
|
cn <- colnames(xdf) |
||||
|
if (!all(c("tactic", "technique") %in% cn)) { |
||||
|
stop("'xdf' needs both 'tactic' and 'technique' columns.", call.=FALSE) |
||||
|
} |
||||
|
|
||||
|
if (!("value" %in% cn)) { |
||||
|
xdf <- dplyr::count(xdf, tactic, technique, name = "value") |
||||
|
} |
||||
|
|
||||
|
if (is.null(input) && is.null(output)) { |
||||
|
if (!is.factor(xdf$tactic)) { |
||||
|
stop( |
||||
|
"No 'input'/'output' transformation specified but 'tactic' is not a factor.", |
||||
|
call.=FALSE |
||||
|
) |
||||
|
} |
||||
|
} else { |
||||
|
if (sum(c(!is.null(input), !is.null(output), !is.null(matrix))) != 3) { |
||||
|
stop("Must specify 'input', 'output', and 'matrix' if any one of them is not NULL", call.=FALSE) |
||||
|
} |
||||
|
xdf$tactic <- fct_tactic(xdf$tactic, input = input, output = output, matrix = matrix) |
||||
|
} |
||||
|
|
||||
|
if (is.null(dark_value_threshold)) dark_value_threshold <- max(xdf$value)/2 |
||||
|
|
||||
|
xdf <- dplyr::arrange(xdf, value) |
||||
|
xdf$technique <- factor(gsub(" ", "\n", xdf$technique)) |
||||
|
xdf <- dplyr::group_by(xdf, tactic) |
||||
|
xdf <- dplyr::mutate(xdf, ids = (n():1)) |
||||
|
xdf <- dplyr::ungroup(xdf) |
||||
|
|
||||
|
gg <- ggplot(xdf, aes(tactic, ids)) |
||||
|
gg <- gg + geom_tile(aes(fill = value), color = tile_col, size = tile_size) |
||||
|
gg <- gg + geom_text( |
||||
|
aes( |
||||
|
label = technique, |
||||
|
color = I(ifelse(value <= dark_value_threshold, dark_lab, light_lab)) |
||||
|
), ... |
||||
|
) |
||||
|
gg <- gg + scale_x_discrete(expand = c(0, 0), position = "top") |
||||
|
gg <- gg + scale_y_reverse(expand = c(0, 0)) |
||||
|
|
||||
|
gg |
||||
|
|
||||
|
} |
@ -0,0 +1,40 @@ |
|||||
|
% Generated by roxygen2: do not edit by hand |
||||
|
% Please edit documentation in R/attck-map.R |
||||
|
\name{attck_map} |
||||
|
\alias{attck_map} |
||||
|
\title{Generate an ATT&CK heatmap} |
||||
|
\usage{ |
||||
|
attck_map(xdf, input = NULL, output = NULL, matrix = NULL, |
||||
|
tile_col = "white", tile_size = 0.5, dark_lab = "white", |
||||
|
light_lab = "black", dark_value_threshold = NULL, ...) |
||||
|
} |
||||
|
\arguments{ |
||||
|
\item{xdf}{a data frame with \code{tactic}, \code{technique} and \code{value} columns. |
||||
|
If no \code{value} column exists, then the function will assume you |
||||
|
have passed in individual events and will perform a "count" |
||||
|
summarization before generating the heatmap.} |
||||
|
|
||||
|
\item{input, output, matrix}{if both are not \code{NULL} then they should be |
||||
|
what \code{\link[=fct_tactic]{fct_tactic()}} takes as parameters. Otherwise, the function |
||||
|
will assume that the \code{tactic} column is already an ordered factor.} |
||||
|
|
||||
|
\item{tile_col, tile_size}{color/size for the tile borders; |
||||
|
defaults to "\code{white}" and \code{0.5}, respectively.} |
||||
|
|
||||
|
\item{dark_lab, light_lab}{text colors for when they appear on top of a |
||||
|
dark or light tile} |
||||
|
|
||||
|
\item{dark_value_threshold}{since you can supply your own fill scale |
||||
|
and may use a transformation (e.g. "\code{log10}") when doing so, you |
||||
|
can specify the cutoff value for when to use \code{dark_lab} vs \code{light_lab}. |
||||
|
If \code{NULL} then half of \code{max(value)} will be used.} |
||||
|
|
||||
|
\item{...}{passed on to the internal call to \code{\link[ggplot2:geom_text]{ggplot2::geom_text()}}} |
||||
|
} |
||||
|
\value{ |
||||
|
a ggplot2 plot object which you can add a fill scale to as well |
||||
|
as themeing. |
||||
|
} |
||||
|
\description{ |
||||
|
Generate an ATT&CK heatmap |
||||
|
} |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 79 KiB |
Loading…
Reference in new issue