├── .github
├── .gitignore
├── dependabot.yml
├── workflows
│ ├── make-release.yaml
│ ├── close-stale-issues.yaml
│ ├── R-CMD-check.yaml
│ ├── build-image.yaml
│ ├── test-coverage.yaml
│ └── pkgdown.yaml
└── ISSUE_TEMPLATE.md
├── codecov.yml
├── LICENSE
├── renv
├── .gitignore
└── settings.dcf
├── inst
├── extdata
│ ├── risk_tolerances.csv
│ ├── qualitative_mappings.csv
│ └── domains.csv
├── survey
│ └── survey.xlsx
├── rmd
│ ├── img
│ │ └── evaluator_hex_48px.png
│ ├── open-sans-import.html
│ └── styles
│ │ ├── word-styles-reference.docx
│ │ ├── open-sans-import.html
│ │ └── html-styles.css
├── openfair_example
│ ├── tests
│ │ ├── openfair_example-expected
│ │ │ └── 001.png
│ │ └── openfair_example.R
│ └── openfair_example.Rmd
├── rstudio
│ └── addins.dcf
├── explore_scenarios
│ └── tests
│ │ └── explore-scenarios.R
├── WORDLIST
└── run_analysis.R
├── data
├── mc_domains.rda
├── mc_mappings.rda
├── mc_capabilities.rda
├── mc_domain_summary.rda
├── mc_scenario_summary.rda
├── mc_simulation_results.rda
├── mc_qualitative_scenarios.rda
└── mc_quantitative_scenarios.rda
├── man
├── figures
│ └── logo.png
├── pipe.Rd
├── tidyrisk_factory.Rd
├── validate_tidyrisk_scenario.Rd
├── print.tidyrisk_scenario.Rd
├── vec_ptype_abbr.tidyrisk_scenario.Rd
├── get_base_fontfamily.Rd
├── dollar_millions.Rd
├── vec_cast.tidyrisk_factor.Rd
├── create_tidyrisk_scenario_skeleton.Rd
├── is_tidyrisk_scenario.Rd
├── as_tibble.tidyrisk_scenario.Rd
├── tidyrisk_factor.Rd
├── mc_domains.Rd
├── import_scenarios.Rd
├── import_capabilities.Rd
├── theme_evaluator.Rd
├── load_data-deprecated.Rd
├── mc_simulation_results.Rd
├── mc_capabilities.Rd
├── sample_tef.Rd
├── split_sheet.Rd
├── summarize_to_disk.Rd
├── mc_mappings.Rd
├── derive_control_key.Rd
├── new_tidyrisk_scenario.Rd
├── openfair_example.Rd
├── identify_outliers.Rd
├── create_templates.Rd
├── read_quantitative_inputs.Rd
├── loss_exceedance_curve.Rd
├── evaluator-package.Rd
├── read_qualitative_inputs.Rd
├── generate_heatmap.Rd
├── sample_lef.Rd
├── import_spreadsheet.Rd
├── sample_vuln.Rd
├── run_simulation.Rd
├── loss_scatterplot.Rd
├── sample_tc.Rd
├── sample_lm.Rd
├── get_mean_control_strength.Rd
├── encode_scenarios.Rd
├── validate_scenarios.Rd
├── mc_quantitative_scenarios.Rd
├── sample_diff.Rd
├── evaluator-deprecated.Rd
├── generate_event_outcomes_plot.Rd
├── mc_qualitative_scenarios.Rd
├── calculate_max_losses.Rd
├── compare_tef_vuln.Rd
├── derive_controls.Rd
├── exposure_histogram.Rd
├── generate_scatterplot-deprecated.Rd
├── openfair_tef_tc_diff_plm_sr.Rd
├── summarize_iterations.Rd
├── risk_dashboard.Rd
├── select_loss_opportunities.Rd
├── summarize_domains.Rd
├── run_simulations.Rd
├── mc_domain_summary.Rd
├── explore_scenarios.Rd
├── summarize_scenario.Rd
├── mc_scenario_summary.Rd
├── openfair_tef_tc_diff_lm.Rd
└── generate_report.Rd
├── pkgdown
├── favicon
│ ├── favicon.ico
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── apple-touch-icon.png
│ ├── apple-touch-icon-60x60.png
│ ├── apple-touch-icon-76x76.png
│ ├── apple-touch-icon-120x120.png
│ ├── apple-touch-icon-152x152.png
│ └── apple-touch-icon-180x180.png
└── extra.css
├── .gitignore
├── scripts
├── run_analysis.R
├── create_templates.R
├── run.R
├── init.R
├── deploy_scenario_explorer.R
└── generate_sample_reports.R
├── tests
├── spelling.R
├── testthat.R
└── testthat
│ ├── test-run-analysis.R
│ ├── test-openfair-example.R
│ ├── test-test-tidyrisk-factor.R
│ ├── test-utils.R
│ ├── test-explore-scenarios.R
│ ├── test-import.R
│ ├── test-load-data.R
│ ├── test-common-graphs.R
│ ├── test-tidyrisk-scenario.R
│ ├── test-simulate.R
│ ├── test-reports.R
│ ├── test-summarize.R
│ ├── test-encode.R
│ └── test-validate.R
├── R
├── utils-pipe.R
├── evaluator-package.R
├── evaluator-deprecated.R
├── create_tidyrisk_skeleton.R
├── tidyrisk_scenario.R
├── simulate.R
├── utils.R
├── validate.R
├── tidyrisk_factor.R
└── data.R
├── meta_tags.html
├── .Rbuildignore
├── evaluator.Rproj
├── CONTRIBUTING.md
├── cran-comments.md
├── Dockerfile
├── DESCRIPTION
├── vignettes
├── customization.Rmd
└── process.Rmd
├── INSTALL_EXTRAS.md
├── data-raw
└── regenerate_data.R
├── _pkgdown.yml
├── CODE_OF_CONDUCT.md
├── NAMESPACE
├── README.md
├── README.Rmd
└── renv.lock
/.github/.gitignore:
--------------------------------------------------------------------------------
1 | *.html
2 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | comment: false
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | YEAR: 2017
2 | COPYRIGHT HOLDER: David F. Severski
3 |
--------------------------------------------------------------------------------
/renv/.gitignore:
--------------------------------------------------------------------------------
1 | local/
2 | lock/
3 | library/
4 | python/
5 | staging/
6 |
--------------------------------------------------------------------------------
/inst/extdata/risk_tolerances.csv:
--------------------------------------------------------------------------------
1 | level,amount
2 | medium,10000000
3 | high,50000000
4 |
--------------------------------------------------------------------------------
/data/mc_domains.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_domains.rda
--------------------------------------------------------------------------------
/data/mc_mappings.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_mappings.rda
--------------------------------------------------------------------------------
/man/figures/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/man/figures/logo.png
--------------------------------------------------------------------------------
/data/mc_capabilities.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_capabilities.rda
--------------------------------------------------------------------------------
/inst/survey/survey.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/inst/survey/survey.xlsx
--------------------------------------------------------------------------------
/data/mc_domain_summary.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_domain_summary.rda
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/favicon.ico
--------------------------------------------------------------------------------
/data/mc_scenario_summary.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_scenario_summary.rda
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .Rproj.user
2 | .Rhistory
3 | .RData
4 | input_files
5 | inst/doc
6 | docs/CNAME
7 | reports
8 | docs
9 |
--------------------------------------------------------------------------------
/data/mc_simulation_results.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_simulation_results.rda
--------------------------------------------------------------------------------
/data/mc_qualitative_scenarios.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_qualitative_scenarios.rda
--------------------------------------------------------------------------------
/data/mc_quantitative_scenarios.rda:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/data/mc_quantitative_scenarios.rda
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/favicon-16x16.png
--------------------------------------------------------------------------------
/pkgdown/favicon/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/favicon-32x32.png
--------------------------------------------------------------------------------
/inst/rmd/img/evaluator_hex_48px.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/inst/rmd/img/evaluator_hex_48px.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon.png
--------------------------------------------------------------------------------
/inst/rmd/open-sans-import.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/inst/rmd/styles/word-styles-reference.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/inst/rmd/styles/word-styles-reference.docx
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon-60x60.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon-76x76.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon-120x120.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon-152x152.png
--------------------------------------------------------------------------------
/pkgdown/favicon/apple-touch-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/pkgdown/favicon/apple-touch-icon-180x180.png
--------------------------------------------------------------------------------
/inst/openfair_example/tests/openfair_example-expected/001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/davidski/evaluator/HEAD/inst/openfair_example/tests/openfair_example-expected/001.png
--------------------------------------------------------------------------------
/scripts/run_analysis.R:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/r
2 |
3 | ## run analysis
4 |
5 | message("Running analysis...")
6 | base_dir <- "/data"
7 | source("/data/run_analysis.R", echo=FALSE)
8 |
--------------------------------------------------------------------------------
/tests/spelling.R:
--------------------------------------------------------------------------------
1 | if(requireNamespace('spelling', quietly = TRUE))
2 | spelling::spell_check_test(vignettes = TRUE, error = FALSE,
3 | skip_on_cran = TRUE)
4 |
--------------------------------------------------------------------------------
/pkgdown/extra.css:
--------------------------------------------------------------------------------
1 | @import url(https://cdn.jsdelivr.net/gh/tonsky/FiraCode@1.206/distr/fira_code.css);
2 |
3 | pre, code {
4 | font-family: "Fira Code", Consolas, Inconsolata, monospace;
5 | }
6 |
--------------------------------------------------------------------------------
/renv/settings.dcf:
--------------------------------------------------------------------------------
1 | external.libraries:
2 | ignored.packages:
3 | package.dependency.fields: Imports, Depends, LinkingTo
4 | snapshot.type: packrat
5 | use.cache: TRUE
6 | vcs.ignore.library: TRUE
7 |
--------------------------------------------------------------------------------
/inst/rstudio/addins.dcf:
--------------------------------------------------------------------------------
1 | Name: Create Tidyrisk Scenario
2 | Description: Insert the skeleton for a tidyrisk scenario in the current document
3 | Binding: create_tidyrisk_scenario_skeleton
4 | Interactive: false
5 |
--------------------------------------------------------------------------------
/inst/rmd/styles/open-sans-import.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/scripts/create_templates.R:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/r
2 |
3 | ## initialize templates
4 |
5 | message("Creating templates...")
6 | library(evaluator)
7 | create_templates("/data")
8 | message("Templates created.")
9 |
10 |
--------------------------------------------------------------------------------
/R/utils-pipe.R:
--------------------------------------------------------------------------------
1 | #' Pipe operator
2 | #'
3 | #' See \code{magrittr::\link[magrittr]{\%>\%}} for details.
4 | #'
5 | #' @name %>%
6 | #' @rdname pipe
7 | #' @keywords internal
8 | #' @export
9 | #' @importFrom magrittr %>%
10 | #' @usage lhs \%>\% rhs
11 | NULL
12 |
--------------------------------------------------------------------------------
/inst/explore_scenarios/tests/explore-scenarios.R:
--------------------------------------------------------------------------------
1 | app <- ShinyDriver$new("../explore_scenarios.Rmd", seed = 7767)
2 | #app <- ShinyDriver$new(here::here("inst/explore_scenarios/explore_scenarios.Rmd", seed = 7767)
3 | app$snapshotInit("explore-scenarios")
4 |
5 | app$snapshot()
6 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | # Maintain dependencies for GitHub Actions
4 | - package-ecosystem: "github-actions"
5 | directory: "/"
6 | schedule:
7 | interval: "weekly"
8 | day: "sunday"
9 | assignees:
10 | - "davidski""
11 |
12 |
--------------------------------------------------------------------------------
/inst/openfair_example/tests/openfair_example.R:
--------------------------------------------------------------------------------
1 | app <- ShinyDriver$new("../openfair_example.Rmd", seed = 95592)
2 | app$snapshotInit("openfair_example")
3 |
4 | app$setInputs(tefml = character(0))
5 | app$setInputs(tefml = 30)
6 | app$setInputs(iterations = 100)
7 | app$setInputs(runmodel = "click")
8 | app$snapshot()
9 |
--------------------------------------------------------------------------------
/man/pipe.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils-pipe.R
3 | \name{\%>\%}
4 | \alias{\%>\%}
5 | \title{Pipe operator}
6 | \usage{
7 | lhs \%>\% rhs
8 | }
9 | \description{
10 | See \code{magrittr::\link[magrittr]{\%>\%}} for details.
11 | }
12 | \keyword{internal}
13 |
--------------------------------------------------------------------------------
/tests/testthat.R:
--------------------------------------------------------------------------------
1 | library(testthat)
2 | library(evaluator)
3 |
4 | # ensure phantom.js is available on CI platforms
5 | if (Sys.getenv("NOT_CRAN", "") != "" || Sys.getenv("CI", "") != "") {
6 | if (!shinytest::dependenciesInstalled()) shinytest:::installDependencies()
7 | message("Using phantom.js from ", shinytest:::find_phantom(), "\n")
8 | }
9 |
10 | test_check("evaluator")
11 |
--------------------------------------------------------------------------------
/scripts/run.R:
--------------------------------------------------------------------------------
1 | library(rmarkdown)
2 |
3 | # Heroku-assigned port to bind
4 | port <- Sys.getenv('PORT')
5 |
6 | # Heroku app mount point
7 | setwd("/app")
8 |
9 | # run Scenario Explorer
10 | rmarkdown::run("explore_scenarios.Rmd",
11 | shiny_args = list(
12 | host = '0.0.0.0',
13 | port = as.numeric(port)
14 | )
15 | )
16 |
--------------------------------------------------------------------------------
/meta_tags.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.Rbuildignore:
--------------------------------------------------------------------------------
1 | ^renv$
2 | ^renv\.lock$
3 | ^CRAN-RELEASE$
4 | ^.*\.Rproj$
5 | ^\.Rproj\.user$
6 | .travis.yml
7 | CODE_OF_CONDUCT.md
8 | CONTRIBUTING.md
9 | INSTALL_EXTRAS.md
10 | README.Rmd
11 | cran-comments.md
12 | ^appveyor\.yml$
13 | ^codecov\.yml$
14 | ^data-raw$
15 | ^docs$
16 | ^_pkgdown\.yml$
17 | ^index.Rmd$
18 | meta_tags.html
19 | ^\.github$
20 | pkgdown
21 | ^reports$
22 | ^scripts$
23 | ^Dockerfile$
24 |
--------------------------------------------------------------------------------
/R/evaluator-package.R:
--------------------------------------------------------------------------------
1 | #' @keywords internal
2 | "_PACKAGE"
3 |
4 | # The following block is used by usethis to automatically manage
5 | # roxygen namespace tags. Modify with care!
6 | ## usethis namespace: start
7 | ## usethis namespace: end
8 | NULL
9 |
10 | ## quiets concerns of R CMD check re: the .'s that appear in pipelines
11 | ## technique from Jenny Bryan's googlesheet package
12 | utils::globalVariables(c("."))
13 |
--------------------------------------------------------------------------------
/R/evaluator-deprecated.R:
--------------------------------------------------------------------------------
1 | #' @title Deprecated functions in package \pkg{evaluator}.
2 | #' @description The functions listed below are deprecated and will be defunct in
3 | #' the near future. When possible, alternative functions with similar
4 | #' functionality are also mentioned. Help pages for deprecated functions are
5 | #' available at \code{help("-deprecated")}.
6 | #' @name evaluator-deprecated
7 | #' @keywords internal
8 | NULL
9 |
--------------------------------------------------------------------------------
/man/tidyrisk_factory.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_factor.R
3 | \name{tidyrisk_factory}
4 | \alias{tidyrisk_factory}
5 | \title{Create a tidyrisk_factor sample function}
6 | \usage{
7 | tidyrisk_factory(factor_label = "TC")
8 | }
9 | \arguments{
10 | \item{factor_label}{abbreviation of the OpenFAIR element}
11 | }
12 | \description{
13 | Create a tidyrisk_factor sample function
14 | }
15 |
--------------------------------------------------------------------------------
/man/validate_tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{validate_tidyrisk_scenario}
4 | \alias{validate_tidyrisk_scenario}
5 | \title{Validates that a scenario object is well formed}
6 | \usage{
7 | validate_tidyrisk_scenario(x)
8 | }
9 | \arguments{
10 | \item{x}{An object}
11 | }
12 | \description{
13 | Validates that a scenario object is well formed
14 | }
15 |
--------------------------------------------------------------------------------
/scripts/init.R:
--------------------------------------------------------------------------------
1 | # init.R
2 | #
3 | # Ensure packages are available for running Scenario Explorer on Heroku
4 | #
5 |
6 | my_packages = c("flexdashboard", "ggplot2", "scales", "viridis",
7 | "stringi", "dplyr", "readr", "tidyr", "modeest", "shiny", "DT")
8 |
9 | install_if_missing = function(p) {
10 | if (p %in% rownames(installed.packages()) == FALSE) {
11 | install.packages(p)
12 | }
13 | }
14 |
15 | invisible(sapply(my_packages, install_if_missing))
16 |
--------------------------------------------------------------------------------
/man/print.tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{print.tidyrisk_scenario}
4 | \alias{print.tidyrisk_scenario}
5 | \title{Default printing of a tidyrisk_scenario}
6 | \usage{
7 | \method{print}{tidyrisk_scenario}(x, ...)
8 | }
9 | \arguments{
10 | \item{x}{A tidyrisk_scenario}
11 |
12 | \item{...}{Currently not used}
13 | }
14 | \description{
15 | Basic printing of a tidyrisk scenario
16 | }
17 |
--------------------------------------------------------------------------------
/man/vec_ptype_abbr.tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{vec_ptype_abbr.tidyrisk_scenario}
4 | \alias{vec_ptype_abbr.tidyrisk_scenario}
5 | \title{Set an abbreviation when displaying an S3 column in a tibble}
6 | \usage{
7 | vec_ptype_abbr.tidyrisk_scenario(x)
8 | }
9 | \arguments{
10 | \item{x}{An object}
11 | }
12 | \description{
13 | Set an abbreviation when displaying an S3 column in a tibble
14 | }
15 |
--------------------------------------------------------------------------------
/.github/workflows/make-release.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | # Sequence of patterns matched against refs/tags
4 | tags:
5 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10
6 |
7 | name: Create Release
8 |
9 | jobs:
10 | build:
11 | name: Create Release
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Checkout code
15 | uses: actions/checkout@v2
16 | - name: Create Release
17 | id: create_release
18 | uses: softprops/action-gh-release@v1
19 |
--------------------------------------------------------------------------------
/man/get_base_fontfamily.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{get_base_fontfamily}
4 | \alias{get_base_fontfamily}
5 | \title{Select a base graphics font family}
6 | \usage{
7 | get_base_fontfamily()
8 | }
9 | \value{
10 | String of the preferred base font.
11 | }
12 | \description{
13 | The Arial Narrow font is preferred. If not available, use a default \code{sans}
14 | family font.
15 | }
16 | \examples{
17 | get_base_fontfamily()
18 | }
19 |
--------------------------------------------------------------------------------
/inst/extdata/qualitative_mappings.csv:
--------------------------------------------------------------------------------
1 | type,label,l,ml,h,conf
2 | tef,frequent,10,24,52,4
3 | tef,occasional,1,6,12,4
4 | tef,rare,0,0.1,1,4
5 | tc,high,0.50,0.75,0.98,3
6 | tc,medium,0.33,0.50,0.60,3
7 | tc,low,0,0.16,0.30,4
8 | diff,5 - Optimized,0.70,0.85,0.98,4
9 | diff,4 - Managed,0.50,0.70,0.84,4
10 | diff,3 - Defined,0.33,0.50,0.60,4
11 | diff,2 - Repeatable,0.20,0.30,0.50,4
12 | diff,1 - Initial,0,0.10,0.30,4
13 | lm,high,1000000,5000000,16000000,4
14 | lm,medium,10000,20000,500000,4
15 | lm,low,100,200,10000,1
16 |
--------------------------------------------------------------------------------
/evaluator.Rproj:
--------------------------------------------------------------------------------
1 | Version: 1.0
2 |
3 | RestoreWorkspace: Default
4 | SaveWorkspace: No
5 | AlwaysSaveHistory: Default
6 |
7 | EnableCodeIndexing: Yes
8 | UseSpacesForTab: Yes
9 | NumSpacesForTab: 2
10 | Encoding: UTF-8
11 |
12 | RnwWeave: Sweave
13 | LaTeX: pdfLaTeX
14 |
15 | AutoAppendNewline: Yes
16 | StripTrailingWhitespace: Yes
17 | LineEndingConversion: Posix
18 |
19 | BuildType: Package
20 | PackageUseDevtools: Yes
21 | PackageInstallArgs: --no-multiarch --with-keep.source
22 | PackageRoxygenize: rd,collate,namespace
23 |
--------------------------------------------------------------------------------
/man/dollar_millions.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{dollar_millions}
4 | \alias{dollar_millions}
5 | \title{Format dollar amounts in terms of millions of USD}
6 | \usage{
7 | dollar_millions(x)
8 | }
9 | \arguments{
10 | \item{x}{A number.}
11 | }
12 | \value{
13 | String in the format of $xM.
14 | }
15 | \description{
16 | Given a number, return a string formatted in terms of millions of dollars.
17 | }
18 | \examples{
19 | dollar_millions(1.523 * 10^6)
20 | }
21 |
--------------------------------------------------------------------------------
/man/vec_cast.tidyrisk_factor.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_factor.R
3 | \name{vec_cast.tidyrisk_factor}
4 | \alias{vec_cast.tidyrisk_factor}
5 | \title{Cast a \code{tidyrisk_factor} vector to a specified type}
6 | \usage{
7 | \method{vec_cast}{tidyrisk_factor}(x, to)
8 | }
9 | \arguments{
10 | \item{x}{Vectors to cast.}
11 |
12 | \item{to}{Type to cast to. If \code{NULL}, \code{x} will be returned as is.}
13 | }
14 | \description{
15 | Cast a \code{tidyrisk_factor} vector to a specified type
16 | }
17 |
--------------------------------------------------------------------------------
/inst/extdata/domains.csv:
--------------------------------------------------------------------------------
1 | domain_id, domain
2 | ISMP, Information Security Management Program
3 | AC, Access Control
4 | HR, Human Resources Security
5 | RISK, Risk Management
6 | POL, Security Policy
7 | ORG, Organization of Information Security
8 | COMP, Compliance
9 | ASSET, Asset Management
10 | PHY, Physical and Environmental Security
11 | OPS, Communications and Operations Management
12 | ADM,"Information Systems Acquisition, Development, and Maintenance"
13 | IM, Information Security Incident Management
14 | BC, Business Continuity Management
15 | PRI, Privacy Practices
--------------------------------------------------------------------------------
/man/create_tidyrisk_scenario_skeleton.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/create_tidyrisk_skeleton.R
3 | \name{create_tidyrisk_scenario_skeleton}
4 | \alias{create_tidyrisk_scenario_skeleton}
5 | \title{Create a skeleton tidyrisk scenario object in the current document}
6 | \usage{
7 | create_tidyrisk_scenario_skeleton()
8 | }
9 | \description{
10 | Inserts a code block into the active document in RStudio for creating a
11 | \link{tidyrisk_scenario} object. This is an easy way of rapidly running
12 | a simulation.
13 | }
14 |
--------------------------------------------------------------------------------
/man/is_tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{is_tidyrisk_scenario}
4 | \alias{is_tidyrisk_scenario}
5 | \title{Test if the object is a tidyrisk_scenario}
6 | \usage{
7 | is_tidyrisk_scenario(x)
8 | }
9 | \arguments{
10 | \item{x}{An object}
11 | }
12 | \value{
13 | \code{TRUE} if the object inherits from the \code{tidyrisk_scenario} class.
14 | }
15 | \description{
16 | This function returns \code{TRUE} for tidyrisk_scenario (or subclasses)
17 | and \code{FALSE} for all other objects.
18 | }
19 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Pull requests are awesome! By participating in this project, you agree to abide by the Evaluator [code of conduct](CODE_OF_CONDUCT.md).
4 |
5 | Fork, then clone the repo:
6 |
7 | git clone https://github.com//evaluator.git
8 |
9 | Make sure the tests pass:
10 |
11 | devtools::test()
12 |
13 | Make your change. Add tests for your change. Make the tests pass:
14 |
15 | devtools::test()
16 |
17 | Push to your fork and [submit a pull request][pr].
18 |
19 | [pr]: https://github.com/davidski/evaluator/compare/
20 |
21 | At this point you're waiting on me!
22 |
--------------------------------------------------------------------------------
/man/as_tibble.tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{as_tibble.tidyrisk_scenario}
4 | \alias{as_tibble.tidyrisk_scenario}
5 | \alias{as.data.frame.tidyrisk_scenario}
6 | \title{Coerce the parameters of a tidyrisk_scenario to a tibble}
7 | \usage{
8 | \method{as_tibble}{tidyrisk_scenario}(x, ...)
9 |
10 | \method{as.data.frame}{tidyrisk_scenario}(x, ...)
11 | }
12 | \arguments{
13 | \item{x}{A tidyrisk_scenario}
14 |
15 | \item{...}{Currently not used}
16 | }
17 | \description{
18 | Coerce the parameters of a tidyrisk_scenario to a tibble
19 | }
20 |
--------------------------------------------------------------------------------
/man/tidyrisk_factor.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_factor.R
3 | \name{tidyrisk_factor}
4 | \alias{tidyrisk_factor}
5 | \alias{new_tidyrisk_factor}
6 | \title{Construct a tidyrisk_factor object}
7 | \usage{
8 | new_tidyrisk_factor(
9 | samples = double(),
10 | factor_label = character(),
11 | details = list()
12 | )
13 |
14 | tidyrisk_factor(samples, factor_label, details = list())
15 | }
16 | \arguments{
17 | \item{samples}{samples}
18 |
19 | \item{factor_label}{fl}
20 |
21 | \item{details}{details}
22 | }
23 | \description{
24 | Construct a tidyrisk_factor object
25 | }
26 |
--------------------------------------------------------------------------------
/man/mc_domains.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_domains}
5 | \alias{mc_domains}
6 | \title{Domain mappings}
7 | \format{
8 | \describe{
9 | \item{domain_id}{abbreviated name of the domain}
10 | \item{domain}{full title of the domain}
11 | }
12 | }
13 | \source{
14 | This is hypothetical information. Any similarity to any other
15 | entity is completely coincidental.
16 | }
17 | \usage{
18 | mc_domains
19 | }
20 | \description{
21 | A sample set of the domains for the demonstration (and artificial)
22 | MetroCare information security program.
23 | }
24 | \keyword{datasets}
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Please briefly describe your problem and what output you expect.
2 |
3 | Please include a minimal reproducible example (AKA a reprex). If you've never
4 | heard of a [reprex](http://reprex.tidyverse.org/) before, start by
5 | reading .
6 |
7 | Including a *sanitized* version of your input files (specificaly, `survey.xlsx`)
8 | is often crucial to allowing others to help identify and correct problems.
9 | If you attach input files, please take care not to include sensitive information
10 | on your submission!
11 |
12 | ---
13 |
14 | Brief description of the problem
15 |
16 | ```r
17 | # insert reprex here
18 | ```
19 |
--------------------------------------------------------------------------------
/man/import_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/import.R
3 | \name{import_scenarios}
4 | \alias{import_scenarios}
5 | \title{Import scenarios from survey spreadsheet}
6 | \usage{
7 | import_scenarios(survey_file = NULL, domains = NULL)
8 | }
9 | \arguments{
10 | \item{survey_file}{Path to survey Excel file. Defaults to a sample file if not supplied.}
11 |
12 | \item{domains}{Dataframe of domains and domain IDs.}
13 | }
14 | \value{
15 | Extracted qualitative scenarios as a dataframe.
16 | }
17 | \description{
18 | Import scenarios from survey spreadsheet
19 | }
20 | \examples{
21 | data(mc_domains)
22 | import_scenarios(domains = mc_domains)
23 | }
24 |
--------------------------------------------------------------------------------
/man/import_capabilities.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/import.R
3 | \name{import_capabilities}
4 | \alias{import_capabilities}
5 | \title{Import capabilities from survey spreadsheet}
6 | \usage{
7 | import_capabilities(survey_file = NULL, domains = NULL)
8 | }
9 | \arguments{
10 | \item{survey_file}{Path to survey Excel file. If not supplied, a default sample file is used.}
11 |
12 | \item{domains}{Dataframe of domains and domain IDs.}
13 | }
14 | \value{
15 | Extracted capabilities as a dataframe.
16 | }
17 | \description{
18 | Import capabilities from survey spreadsheet
19 | }
20 | \examples{
21 | data(mc_domains)
22 | import_capabilities(domains = mc_domains)
23 | }
24 |
--------------------------------------------------------------------------------
/man/theme_evaluator.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{theme_evaluator}
4 | \alias{theme_evaluator}
5 | \title{Default ggplot theme used by all Evaluator-supplied graphics}
6 | \usage{
7 | theme_evaluator(base_family = "")
8 | }
9 | \arguments{
10 | \item{base_family}{Font family.}
11 | }
12 | \value{
13 | A ggplot theme object.
14 | }
15 | \description{
16 | Returns a standardized ggplot theme used by all built-in Evaluator plots.
17 | }
18 | \examples{
19 | library(ggplot2)
20 | p <- ggplot(mtcars) + geom_point(aes(wt, mpg, color = factor(gear))) + facet_wrap(~am)
21 | font_family <- get_base_fontfamily()
22 | p + theme_evaluator(font_family)
23 | }
24 |
--------------------------------------------------------------------------------
/man/load_data-deprecated.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/load_data.R
3 | \name{load_data-deprecated}
4 | \alias{load_data-deprecated}
5 | \title{Load input and results files}
6 | \arguments{
7 | \item{input_directory}{Location of input files.}
8 |
9 | \item{results_directory}{Location of simulation results.}
10 | }
11 | \value{
12 | List of all key data objects.
13 | }
14 | \description{
15 | Given an input directory and a directory of simulation results, load all
16 | of the key Evaluator data objects into memory.
17 | }
18 | \examples{
19 | \dontrun{
20 | load_data("~/evaluator/inputs", "~/evaluator/results")
21 | }
22 |
23 | }
24 | \seealso{
25 | \code{\link{evaluator-deprecated}}
26 | }
27 | \keyword{internal}
28 |
--------------------------------------------------------------------------------
/tests/testthat/test-run-analysis.R:
--------------------------------------------------------------------------------
1 | context("Analysis Script")
2 |
3 | tmpdir <- tempdir()
4 | tmpwork <- file.path(tmpdir, "analysis")
5 |
6 | test_that("Minimum Viable Analysis script works", {
7 |
8 | # this is expensive to run, skip if the EVALUATOR_TEST_MVA envvar is not set
9 | skip_if_not(Sys.getenv("EVALUATOR_TEST_MVA") == TRUE,
10 | "Not running MVA test - EVALUATOR_TEST_MVA not set")
11 |
12 | create_templates(tmpwork)
13 | base_dir <- tmpwork
14 | source(system.file("run_analysis.R", package = "evaluator"), local = TRUE)
15 | expect_equivalent(file.exists(file.path(base_dir, "results", "risk_dashboard.html")), TRUE)
16 | expect_equivalent(file.exists(file.path(base_dir, "results", "risk_report.docx")), TRUE)
17 | })
18 |
19 | unlink(tmpwork, recursive = TRUE)
20 |
--------------------------------------------------------------------------------
/man/mc_simulation_results.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_simulation_results}
5 | \alias{mc_simulation_results}
6 | \title{Simulation results}
7 | \format{
8 | \describe{
9 | \item{scenario_id}{id of the scenario}
10 | \item{domain_id}{domain id}
11 | \item{results}{nested data frame of simulation results for the scenario}
12 | }
13 | }
14 | \source{
15 | This is hypothetical information. Any similarity to any other
16 | entity is completely coincidental.
17 | }
18 | \usage{
19 | mc_simulation_results
20 | }
21 | \description{
22 | A sample set of information security risk scenario simulation results
23 | for the demonstration (and artificial) MetroCare information security
24 | program.
25 | }
26 | \keyword{datasets}
27 |
--------------------------------------------------------------------------------
/man/mc_capabilities.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_capabilities}
5 | \alias{mc_capabilities}
6 | \title{Capabilities}
7 | \format{
8 | \describe{
9 | \item{capability_id}{unique id of the capability}
10 | \item{domain_id}{domain id to which the capability applies}
11 | \item{capability}{full text summary of the capability}
12 | \item{diff}{qualitative label of control effectiveness}
13 | }
14 | }
15 | \source{
16 | This is hypothetical information. Any similarity to any other
17 | entity is completely coincidental.
18 | }
19 | \usage{
20 | mc_capabilities
21 | }
22 | \description{
23 | A sample set of capabilities for the demonstration (and artificial)
24 | MetroCare information security program.
25 | }
26 | \keyword{datasets}
27 |
--------------------------------------------------------------------------------
/man/sample_tef.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_tef}
4 | \alias{sample_tef}
5 | \title{Calculate the number of simulated threat event frequencies (TEF)}
6 | \usage{
7 | sample_tef(n, params = NULL, .func = NULL)
8 | }
9 | \arguments{
10 | \item{n}{Number of samples to generate.}
11 |
12 | \item{params}{Optional parameters to pass to \code{.func}.}
13 |
14 | \item{.func}{Function to use to simulate TEF, defaults to \code{\link[mc2d]{rpert}}.}
15 | }
16 | \value{
17 | List containing type ("tef"), samples (as a vector), and details (as a list).
18 | }
19 | \description{
20 | Calculate the number of simulated threat event frequencies (TEF)
21 | }
22 | \seealso{
23 | Other OpenFAIR models:
24 | \code{\link{openfair_tef_tc_diff_plm_sr}()}
25 | }
26 | \concept{OpenFAIR models}
27 |
--------------------------------------------------------------------------------
/.github/workflows/close-stale-issues.yaml:
--------------------------------------------------------------------------------
1 | name: 'Close stale issues and PR'
2 | on:
3 | schedule:
4 | - cron: '30 1 * * *'
5 |
6 | jobs:
7 | stale:
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/stale@v4
11 | with:
12 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days.'
13 | stale-pr-message: 'This PR is stale because it has been open 45 days with no activity. Remove stale label or comment or this will be closed in 10 days.'
14 | close-issue-message: 'This issue was closed because it has been stalled for 5 days with no activity.'
15 | days-before-stale: 60
16 | days-before-close: 7
17 | days-before-pr-close: -1
18 | exempt-issue-labels: preserve
19 |
20 |
--------------------------------------------------------------------------------
/man/split_sheet.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/import.R
3 | \name{split_sheet}
4 | \alias{split_sheet}
5 | \title{Split a sheet of the survey spreadsheet into either capabilities or threats}
6 | \usage{
7 | split_sheet(dat, table_type = "capabilities")
8 | }
9 | \arguments{
10 | \item{dat}{Raw sheet input from \code{\link[readxl]{read_excel}}.}
11 |
12 | \item{table_type}{Either \code{capabilities} or \code{threats}}
13 | }
14 | \value{
15 | Extracted table as a dataframe
16 | }
17 | \description{
18 | The default data collection Excel spreadsheet solicits threat
19 | scenarios and applicable controls for each domain. This function
20 | takes a single sheet from the spreadsheet, as read by \code{\link[readxl]{read_excel}}
21 | and pulls out either the capabilities or threats, as directed by the
22 | user.
23 | }
24 |
--------------------------------------------------------------------------------
/man/summarize_to_disk.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/summarize.R
3 | \name{summarize_to_disk}
4 | \alias{summarize_to_disk}
5 | \title{Create all summary files and write to disk}
6 | \usage{
7 | summarize_to_disk(simulation_results, results_dir)
8 | }
9 | \arguments{
10 | \item{simulation_results}{Simulation results dataframe.}
11 |
12 | \item{results_dir}{Directory to place simulation files.}
13 | }
14 | \value{
15 | Tibble with paths to the created data files.
16 | }
17 | \description{
18 | This is a wrapper around \code{\link{summarize_scenario}} and
19 | \code{\link{summarize_domains}}, calling both functions and writing the
20 | dataframes to a location on disk.
21 | }
22 | \examples{
23 | \dontrun{
24 | data(mc_simulation_results)
25 | summarize_to_disk(mc_simulation_results, results_dir = tempdir())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/man/mc_mappings.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_mappings}
5 | \alias{mc_mappings}
6 | \title{Qualitative to quantitative mappings}
7 | \format{
8 | \describe{
9 | \item{type}{The element in the OpenFAIR ontology to which this mapping applies}
10 | \item{label}{Qualitative label}
11 | \item{l}{BetaPERT low value}
12 | \item{ml}{BetaPERT most likely value}
13 | \item{h}{BetaPERT high value}
14 | \item{conf}{BetaPERT confidence value}
15 | }
16 | }
17 | \source{
18 | This is hypothetical information. Any similarity to any other
19 | entity is completely coincidental.
20 | }
21 | \usage{
22 | mc_mappings
23 | }
24 | \description{
25 | A sample set of qualitative to quantitative mappings for the demonstration
26 | (and artificial) MetroCare information security program.
27 | }
28 | \keyword{datasets}
29 |
--------------------------------------------------------------------------------
/inst/WORDLIST:
--------------------------------------------------------------------------------
1 | Afer
2 | Aguta
3 | AppVeyor
4 | Arial
5 | BetaPERT
6 | CMM
7 | COBIT
8 | CONF
9 | CSF
10 | customizations
11 | deprecations
12 | dplyr
13 | DSS
14 | DT
15 | Ezeugo
16 | flexdashboard
17 | ggplot
18 | HITRUST
19 | https
20 | lef
21 | LEF
22 | lm
23 | LM
24 | MacBook
25 | MetroCare
26 | NIST
27 | ogsys
28 | OpenFAIR
29 | opengroup
30 | ORCID
31 | overridable
32 | pandoc
33 | PCI
34 | PLM
35 | pre
36 | rda
37 | rds
38 | reimport
39 | repo
40 | reproducability
41 | RiskLens
42 | rmarkdown
43 | Rmarkdown
44 | RMarkdown
45 | roxygen
46 | RPubs
47 | RStudio
48 | screencast
49 | shinytest
50 | severski
51 | signoff
52 | SIPMath
53 | SIRA
54 | SLE
55 | SLM
56 | statip
57 | subclasses
58 | summarization
59 | tc
60 | TCap
61 | tef
62 | TEF
63 | Tibble
64 | tibble
65 | tibbles
66 | tidyrisk
67 | tidyverse
68 | Unnest
69 | unnest
70 | VaR
71 | VeryLow
72 | vuln
73 | VULN
74 | www
75 | xlsx
76 | xM
77 |
--------------------------------------------------------------------------------
/man/derive_control_key.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/encode.R
3 | \name{derive_control_key}
4 | \alias{derive_control_key}
5 | \title{Derive control ID to control description mappings}
6 | \usage{
7 | derive_control_key(capability_ids, capabilities)
8 | }
9 | \arguments{
10 | \item{capability_ids}{Comma-delimited list of capabilities in scope for a scenario.}
11 |
12 | \item{capabilities}{Dataframe of master list of all qualitative capabilities.}
13 | }
14 | \value{
15 | A named list of control IDs and descriptions.
16 | }
17 | \description{
18 | Given a comma-separated list of control IDs, return a named list
19 | of descriptions for each control with the names set to the control
20 | IDs.
21 | }
22 | \examples{
23 | data(mc_capabilities)
24 | capability_ids <- c("CAP-01", "CAP-03")
25 | derive_control_key(capability_ids, mc_capabilities)
26 | }
27 |
--------------------------------------------------------------------------------
/man/new_tidyrisk_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/tidyrisk_scenario.R
3 | \name{new_tidyrisk_scenario}
4 | \alias{new_tidyrisk_scenario}
5 | \alias{tidyrisk_scenario}
6 | \title{Construct a quantitative scenario object}
7 | \usage{
8 | new_tidyrisk_scenario(..., model = "openfair_tef_tc_diff_lm")
9 |
10 | tidyrisk_scenario(..., model = "openfair_tef_tc_diff_lm")
11 | }
12 | \arguments{
13 | \item{...}{One or more named OpenFAIR factor with parameters for sampling}
14 |
15 | \item{model}{Name of model to run}
16 | }
17 | \description{
18 | Supply one or more named lists in the format of \code{foo_params},
19 | where each \code{foo} is an OpenFAIR factor name (e.g. tef, tc, diff, lm).
20 | Each factor should include a function name (\code{func}) to which the
21 | other named elements in the list are passed as parameters when
22 | sampling.
23 | }
24 |
--------------------------------------------------------------------------------
/man/openfair_example.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/report.R
3 | \name{openfair_example}
4 | \alias{openfair_example}
5 | \title{Launch OpenFAIR demonstration web application}
6 | \usage{
7 | openfair_example(intermediates_dir = tempdir(), quiet = TRUE)
8 | }
9 | \arguments{
10 | \item{intermediates_dir}{Location for intermediate knit files.}
11 |
12 | \item{quiet}{\code{TRUE} to suppress printing of pandoc output.}
13 | }
14 | \value{
15 | Invisible NULL
16 | }
17 | \description{
18 | A simple web application to demonstrate OpenFAIR modeling. This application
19 | allows a user to enter beta PERT parameters and run simulations to see the
20 | distribution of results, with high level summary statistics. As a demonstration
21 | application, only TEF, TC, DIFF, and LM parameters may be entered.
22 | }
23 | \examples{
24 | \dontrun{
25 | openfair_example()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/cran-comments.md:
--------------------------------------------------------------------------------
1 | ## Update
2 |
3 | This is a minor update, removing some dependencies and substituting others
4 | with SUGGESTS when full removals could not easily be supported. This is in
5 | response to CRAN notices of incompatibility with a forthcoming knitr
6 | release.
7 |
8 | ## Test environments
9 |
10 | * local Windows 10 64 install, R 4.1
11 | * local OS X, R 4.1
12 | * OS X (via GitHub Actions), R-release
13 | * win-builder (R-devel and R-release)
14 | * R-Hub
15 | * "devian-gcc-devel-nold" - R-devel --enable-long-double=no
16 | * Windows (R-release and R-devel)
17 | * Linux (R-release and R-devel)
18 |
19 | ## R CMD check results
20 |
21 | * There are no NOTES, ERRORS, or WARNINGS.
22 |
23 | ## Downstream dependencies
24 |
25 | The `collector` package (also authored by me) depends on this package.
26 | `collector` is currently archived on CRAN, but a resubmission of this
27 | package is in process.
28 |
--------------------------------------------------------------------------------
/man/identify_outliers.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{identify_outliers}
4 | \alias{identify_outliers}
5 | \title{Unnest a summarized results dataframe, adding outlier information}
6 | \usage{
7 | identify_outliers(results)
8 | }
9 | \arguments{
10 | \item{results}{Scenario summary results}
11 | }
12 | \value{
13 | The supplied dataframe with the following additional columns:
14 | \itemize{
15 | \item \code{ale_var_zscore} - Annual loss z-score
16 | \item \code{outlier} - Logical flag when the z-score is greater than or equal to two
17 | }
18 | }
19 | \description{
20 | Given a summarized results dataframe, unnest the summary results
21 | column and use the value at risk (VaR) column to identify all the
22 | elements that are outliers (having a VaR >= two standard deviations)
23 | }
24 | \examples{
25 | data(mc_scenario_summary)
26 | identify_outliers(mc_scenario_summary)
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/R-CMD-check.yaml:
--------------------------------------------------------------------------------
1 | # For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag.
2 | # https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions
3 | on:
4 | push:
5 | branches:
6 | - main
7 | - master
8 | pull_request:
9 | branches:
10 | - main
11 | - master
12 |
13 | name: R-CMD-check
14 |
15 | jobs:
16 | R-CMD-check:
17 | runs-on: macOS-latest
18 | steps:
19 | - uses: actions/checkout@v2
20 | - uses: r-lib/actions/setup-pandoc@v1
21 | - uses: r-lib/actions/setup-r@v1
22 | - name: Install dependencies
23 | run: |
24 | install.packages(c("remotes", "rcmdcheck"))
25 | remotes::install_deps(dependencies = TRUE)
26 | shell: Rscript {0}
27 | - name: Check
28 | run: rcmdcheck::rcmdcheck(args = "--no-manual", error_on = "error")
29 | shell: Rscript {0}
30 |
--------------------------------------------------------------------------------
/tests/testthat/test-openfair-example.R:
--------------------------------------------------------------------------------
1 | context("OpenFAIR Example Shiny app")
2 |
3 | library(shinytest)
4 |
5 | test_that("openfairExample() works", {
6 | skip("Shiny testing is awkward, consider moving to stand alone pkg")
7 |
8 | # Don't run these tests on the CRAN build servers
9 | skip_on_cran()
10 |
11 | skip_if_not(rmarkdown::pandoc_available(),
12 | message = "Cannot run shinytest without pandoc available.")
13 |
14 | # Don't run on r-devel, which behaves badly on TravisCI
15 | skip_if(grepl("unstable", R.version.string),
16 | message = "Shinytest gives unpredictable results on R-devel")
17 |
18 | # Use compareImages=FALSE because the expected image screenshots were created
19 | # on a Mac, and they will differ from screenshots taken on the CI platform,
20 | # which runs on Linux.
21 | appdir <- system.file(package = "evaluator", "openfair_example")
22 |
23 | expect_pass(testApp(appdir, compareImages = FALSE))
24 | })
25 |
--------------------------------------------------------------------------------
/man/create_templates.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/load_data.R
3 | \name{create_templates}
4 | \alias{create_templates}
5 | \title{Create a directory structure for risk analysis, pre-populated with templates}
6 | \usage{
7 | create_templates(base_directory)
8 | }
9 | \arguments{
10 | \item{base_directory}{Parent directory under which to create starter files.}
11 | }
12 | \value{
13 | A dataframe of the starter filenames, along with a flag on whether a file was copied.
14 | }
15 | \description{
16 | Copy the sample files into an \code{inputs} subdirectory. This makes the starter
17 | files available for customizing and data collection. The \code{inputs}
18 | directory will be created if not already present. Preexisting files, if
19 | present, will not be overwritten. Also creates an empty \code{results}
20 | subdirectory as a default location for evaluator output.
21 | }
22 | \examples{
23 | \dontrun{
24 | create_templates("~/evaluator")
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/man/read_quantitative_inputs.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/load_data.R
3 | \name{read_quantitative_inputs}
4 | \alias{read_quantitative_inputs}
5 | \title{Load quantitative inputs}
6 | \usage{
7 | read_quantitative_inputs(input_directory = "~/evaluator/inputs")
8 | }
9 | \arguments{
10 | \item{input_directory}{Location of input files.}
11 | }
12 | \value{
13 | List of domains, quantitative_scenarios, and risk_tolerances
14 | }
15 | \description{
16 | Given an input directory, load the quantitative objects into memory.
17 | }
18 | \details{
19 | The key quantitative inputs for Evaluator processing include:
20 | \itemize{
21 | \item \code{domains.csv} - domains and domain_ids
22 | \item \code{risk_tolerances.csv} - the risk tolerances of the organization
23 | \item \code{quantitative_scenarios.rds} - risk scenarios and quantified parameters
24 | }
25 | }
26 | \examples{
27 | \dontrun{
28 | read_quantitative_inputs("~/evaluator/inputs")
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/man/loss_exceedance_curve.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{loss_exceedance_curve}
4 | \alias{loss_exceedance_curve}
5 | \title{Display the loss exceedance curve for a group of one or more scenarios}
6 | \usage{
7 | loss_exceedance_curve(iteration_results)
8 | }
9 | \arguments{
10 | \item{iteration_results}{Iteration-level summary from \code{summarize_iterations}.}
11 | }
12 | \value{
13 | A ggplot object.
14 | }
15 | \description{
16 | Display the loss exceedance curve for a group of one or more scenarios
17 | }
18 | \examples{
19 | data(mc_simulation_results)
20 | summarize_iterations(mc_simulation_results$results) \%>\% loss_exceedance_curve()
21 | }
22 | \seealso{
23 | Other result graphs:
24 | \code{\link{exposure_histogram}()},
25 | \code{\link{generate_event_outcomes_plot}()},
26 | \code{\link{generate_heatmap}()},
27 | \code{\link{generate_scatterplot-deprecated}},
28 | \code{\link{loss_scatterplot}()}
29 | }
30 | \concept{result graphs}
31 |
--------------------------------------------------------------------------------
/man/evaluator-package.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/evaluator-package.R
3 | \docType{package}
4 | \name{evaluator-package}
5 | \alias{evaluator}
6 | \alias{evaluator-package}
7 | \title{evaluator: Quantified Risk Assessment Toolkit}
8 | \description{
9 | \if{html}{\figure{logo.png}{options: align='right' alt='logo' width='120'}}
10 |
11 | An open source risk analysis toolkit based on the OpenFAIR ontology and risk analysis standard . Empowers an organization to perform a quantifiable, repeatable, and data-driven risk review.
12 | }
13 | \seealso{
14 | Useful links:
15 | \itemize{
16 | \item \url{https://evaluator.tidyrisk.org}
17 | \item Report bugs at \url{https://github.com/davidski/evaluator/issues}
18 | }
19 |
20 | }
21 | \author{
22 | \strong{Maintainer}: David Severski \email{davidski@deadheaven.com} (\href{https://orcid.org/0000-0001-7867-0459}{ORCID})
23 |
24 | }
25 | \keyword{internal}
26 |
--------------------------------------------------------------------------------
/man/read_qualitative_inputs.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/load_data.R
3 | \name{read_qualitative_inputs}
4 | \alias{read_qualitative_inputs}
5 | \title{Load qualitative inputs}
6 | \usage{
7 | read_qualitative_inputs(input_directory = "~/evaluator/inputs")
8 | }
9 | \arguments{
10 | \item{input_directory}{Location of input files.}
11 | }
12 | \value{
13 | List of domains, mappings, capabilities, and qualitative_scenarios
14 | }
15 | \description{
16 | Given an input directory, load the key qualitative objects into memory.
17 | }
18 | \details{
19 | The key qualitative inputs for Evaluator processing include:
20 | \itemize{
21 | \item \code{domains.csv}: domains and domain_ids
22 | \item \code{mappings.csv}: qualitative to quantitative mappings
23 | \item \code{capabilities.csv}: qualitative capabilities
24 | \item \code{qualitative_scenarios.csv}: qualitative risk scenarios
25 | }
26 | }
27 | \examples{
28 | \dontrun{
29 | read_qualitative_inputs("~/evaluator/inputs")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/man/generate_heatmap.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{generate_heatmap}
4 | \alias{generate_heatmap}
5 | \title{Display a heatmap of impact by domain}
6 | \usage{
7 | generate_heatmap(domain_summary)
8 | }
9 | \arguments{
10 | \item{domain_summary}{Simulations summarized at a domain level via \code{summarize_domains}.}
11 | }
12 | \value{
13 | A ggplot object.
14 | }
15 | \description{
16 | Given a domain_summary and a list of all domains, generate a heatmap colored
17 | by the 95\% VaR. This plot displays the domains in which aggregate risk is
18 | greater than others.
19 | }
20 | \examples{
21 | data(mc_domain_summary)
22 | generate_heatmap(mc_domain_summary)
23 | }
24 | \seealso{
25 | Other result graphs:
26 | \code{\link{exposure_histogram}()},
27 | \code{\link{generate_event_outcomes_plot}()},
28 | \code{\link{generate_scatterplot-deprecated}},
29 | \code{\link{loss_exceedance_curve}()},
30 | \code{\link{loss_scatterplot}()}
31 | }
32 | \concept{result graphs}
33 |
--------------------------------------------------------------------------------
/man/sample_lef.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_lef}
4 | \alias{sample_lef}
5 | \title{Sample loss event frequency}
6 | \usage{
7 | sample_lef(n, .func = NULL, params = NULL)
8 | }
9 | \arguments{
10 | \item{n}{Number of samples to generate.}
11 |
12 | \item{.func}{Function to use to simulate LEF, defaults to \code{\link[stats]{rnorm}}.}
13 |
14 | \item{params}{Optional parameters to pass to \code{.func}.}
15 | }
16 | \value{
17 | List containing type ("lef"), samples (as a vector), and details (as a list).
18 | }
19 | \description{
20 | Sample loss event frequency
21 | }
22 | \seealso{
23 | Other OpenFAIR helpers:
24 | \code{\link{compare_tef_vuln}()},
25 | \code{\link{get_mean_control_strength}()},
26 | \code{\link{openfair_tef_tc_diff_lm}()},
27 | \code{\link{sample_diff}()},
28 | \code{\link{sample_lm}()},
29 | \code{\link{sample_tc}()},
30 | \code{\link{sample_vuln}()},
31 | \code{\link{select_loss_opportunities}()}
32 | }
33 | \concept{OpenFAIR helpers}
34 |
--------------------------------------------------------------------------------
/man/import_spreadsheet.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/import.R
3 | \name{import_spreadsheet}
4 | \alias{import_spreadsheet}
5 | \title{Import the scenario spreadsheet}
6 | \usage{
7 | import_spreadsheet(
8 | survey_file = system.file("survey", "survey.xlsx", package = "evaluator"),
9 | domains = NULL,
10 | output_dir = "~/evaluator/results"
11 | )
12 | }
13 | \arguments{
14 | \item{survey_file}{Path to survey Excel file. Defaults to an Evaluator-provided sample spreadsheet.}
15 |
16 | \item{domains}{Dataframe of domains and domain IDs. Defaults to built-in sample \code{domains} dataset.}
17 |
18 | \item{output_dir}{Output file directory.}
19 | }
20 | \value{
21 | Dataframe of file information on the two newly created files.
22 | }
23 | \description{
24 | This is a convenience wrapper around the \code{\link{import_scenarios}} and
25 | \code{\link{import_capabilities}} functions. Writes cleaned
26 | comma-separated formatted files for the scenarios and
27 | capabilities to disk.
28 | }
29 |
--------------------------------------------------------------------------------
/man/sample_vuln.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_vuln}
4 | \alias{sample_vuln}
5 | \title{Calculate the vulnerability}
6 | \usage{
7 | sample_vuln(n, .func = NULL, params = NULL)
8 | }
9 | \arguments{
10 | \item{n}{Number of samples to generate.}
11 |
12 | \item{.func}{Function to use to simulate VULN, defaults to \code{\link[stats]{rbinom}}.}
13 |
14 | \item{params}{Optional parameters to pass to \code{.func}.}
15 | }
16 | \value{
17 | List containing type ("vuln"), samples (as a vector), and details (as a list).
18 | }
19 | \description{
20 | Calculate the vulnerability
21 | }
22 | \seealso{
23 | Other OpenFAIR helpers:
24 | \code{\link{compare_tef_vuln}()},
25 | \code{\link{get_mean_control_strength}()},
26 | \code{\link{openfair_tef_tc_diff_lm}()},
27 | \code{\link{sample_diff}()},
28 | \code{\link{sample_lef}()},
29 | \code{\link{sample_lm}()},
30 | \code{\link{sample_tc}()},
31 | \code{\link{select_loss_opportunities}()}
32 | }
33 | \concept{OpenFAIR helpers}
34 |
--------------------------------------------------------------------------------
/man/run_simulation.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simulate.R
3 | \name{run_simulation}
4 | \alias{run_simulation}
5 | \title{Run simulations for a scenario}
6 | \usage{
7 | run_simulation(
8 | scenario,
9 | iterations = 10000L,
10 | ale_maximum = NULL,
11 | verbose = FALSE,
12 | simulation_count = NULL
13 | )
14 | }
15 | \arguments{
16 | \item{scenario}{A \link{tidyrisk_scenario} object.}
17 |
18 | \item{iterations}{Number of iterations to run on each scenario.}
19 |
20 | \item{ale_maximum}{Maximum practical annual losses.}
21 |
22 | \item{verbose}{Whether verbose console output is requested.}
23 |
24 | \item{simulation_count}{\strong{DEPRECATED} Number of simulations to perform.}
25 | }
26 | \value{
27 | Dataframe of results.
28 | }
29 | \description{
30 | Given a quantitative scenario object of type \code{tidyrisk_scenario}, run an
31 | OpenFAIR Monte Carlo simulation.
32 | }
33 | \examples{
34 | data(mc_quantitative_scenarios)
35 | run_simulation(mc_quantitative_scenarios$scenario[[1]], 10)
36 | }
37 |
--------------------------------------------------------------------------------
/R/create_tidyrisk_skeleton.R:
--------------------------------------------------------------------------------
1 | #' Create a skeleton tidyrisk scenario object in the current document
2 | #'
3 | #' Inserts a code block into the active document in RStudio for creating a
4 | #' \link{tidyrisk_scenario} object. This is an easy way of rapidly running
5 | #' a simulation.
6 | #'
7 | #' @export
8 | #' @importFrom rstudioapi insertText
9 | create_tidyrisk_scenario_skeleton <- function() {
10 | rstudioapi::insertText(text =
11 | "my_scenario <- tidyrisk_scenario(
12 | tef_params = list(min = 1, mode = 10, max = 100, shape = 3, func = \"mc2d::rpert\"),
13 | tc_params = list(min = .20, mode = .30, max = .70, shape = 2, func = \"mc2d::rpert\"),
14 | # note that diff_params can take multiple controls, hence the nested list
15 | diff_params = list(list(min = .25, mode = .50, max = .60, shape = 3, func = \"mc2d::rpert\")),
16 | lm_params = list(min = 100, mode = 1000, max = 10000, shape = 3, func = \"mc2d::rpert\"))
17 |
18 | my_results <- run_simulation(my_scenario, iterations = 1000)
19 | "
20 | )
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/man/loss_scatterplot.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{loss_scatterplot}
4 | \alias{loss_scatterplot}
5 | \title{Display a scatterplot of loss events for a scenario}
6 | \usage{
7 | loss_scatterplot(simulation_result)
8 | }
9 | \arguments{
10 | \item{simulation_result}{Simulation results from \code{run_simulation}.}
11 | }
12 | \value{
13 | A ggplot object.
14 | }
15 | \description{
16 | Given a detailed results dataframe create a scatterplot of the number of
17 | loss events versus the total amount of expected annual loss for each
18 | simulation. This provides a detailed view on the results.
19 | }
20 | \examples{
21 | data(mc_simulation_results)
22 | loss_scatterplot(mc_simulation_results$results[[1]])
23 | }
24 | \seealso{
25 | Other result graphs:
26 | \code{\link{exposure_histogram}()},
27 | \code{\link{generate_event_outcomes_plot}()},
28 | \code{\link{generate_heatmap}()},
29 | \code{\link{generate_scatterplot-deprecated}},
30 | \code{\link{loss_exceedance_curve}()}
31 | }
32 | \concept{result graphs}
33 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rocker/tidyverse:4.0.3 as builder
2 |
3 | ARG EVALUATOR_VERSION
4 | ENV BUILD_DATE=2020-11-27
5 | ARG ADD=shiny
6 |
7 | LABEL org.opencontainers.image.licenses="MIT" \
8 | org.opencontainers.image.source="https://github.com/davidski/evaluator" \
9 | org.opencontainers.image.documentation="https://evaluator.tidyrisk.org" \
10 | maintainer="David F. Severski " \
11 | org.openctainers.image.authors="David F. Severski "
12 |
13 | RUN /rocker_scripts/install_shiny_server.sh
14 |
15 | COPY . /src/
16 | WORKDIR /src
17 |
18 | RUN apt-get update \
19 | && apt-get install -y zlib1g-dev libproj-dev libpng-dev libxml2-dev \
20 | && install2.r --deps=TRUE remotes \
21 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
22 |
23 | RUN apt-get clean \
24 | && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
25 |
26 | COPY /scripts/create_templates.R /usr/local/bin/create_templates
27 | COPY /scripts/run_analysis.R /usr/local/bin/run_analysis
28 |
29 | VOLUME /data
30 |
31 | EXPOSE 8787
32 | EXPOSE 3838
33 |
--------------------------------------------------------------------------------
/man/sample_tc.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_tc}
4 | \alias{sample_tc}
5 | \title{Sample threat capabilities (TC) from a distribution function}
6 | \usage{
7 | sample_tc(n, params = NULL, .func = NULL)
8 | }
9 | \arguments{
10 | \item{n}{Number of samples to generate.}
11 |
12 | \item{params}{Optional parameters to pass to \code{.func}.}
13 |
14 | \item{.func}{Function to use to simulate TC, defaults to \code{\link[mc2d]{rpert}}.}
15 | }
16 | \value{
17 | List containing type ("tc"), samples (as a vector), and details (as a list).
18 | }
19 | \description{
20 | Sample threat capabilities (TC) from a distribution function
21 | }
22 | \seealso{
23 | Other OpenFAIR helpers:
24 | \code{\link{compare_tef_vuln}()},
25 | \code{\link{get_mean_control_strength}()},
26 | \code{\link{openfair_tef_tc_diff_lm}()},
27 | \code{\link{sample_diff}()},
28 | \code{\link{sample_lef}()},
29 | \code{\link{sample_lm}()},
30 | \code{\link{sample_vuln}()},
31 | \code{\link{select_loss_opportunities}()}
32 | }
33 | \concept{OpenFAIR helpers}
34 |
--------------------------------------------------------------------------------
/man/sample_lm.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_lm}
4 | \alias{sample_lm}
5 | \title{Given a number of loss events and a loss distribution, calculate losses}
6 | \usage{
7 | sample_lm(n, .func = NULL, params = NULL)
8 | }
9 | \arguments{
10 | \item{n}{Number of samples to generate.}
11 |
12 | \item{.func}{Function to use to simulate TEF, defaults to \code{\link[mc2d]{rpert}}.}
13 |
14 | \item{params}{Optional parameters to pass to \code{.func}.}
15 | }
16 | \value{
17 | List containing type ("lm"), samples (as a vector), and details (as a list).
18 | }
19 | \description{
20 | Given a number of loss events and a loss distribution, calculate losses
21 | }
22 | \seealso{
23 | Other OpenFAIR helpers:
24 | \code{\link{compare_tef_vuln}()},
25 | \code{\link{get_mean_control_strength}()},
26 | \code{\link{openfair_tef_tc_diff_lm}()},
27 | \code{\link{sample_diff}()},
28 | \code{\link{sample_lef}()},
29 | \code{\link{sample_tc}()},
30 | \code{\link{sample_vuln}()},
31 | \code{\link{select_loss_opportunities}()}
32 | }
33 | \concept{OpenFAIR helpers}
34 |
--------------------------------------------------------------------------------
/scripts/deploy_scenario_explorer.R:
--------------------------------------------------------------------------------
1 | library(rsconnect)
2 | rsconnect::setAccountInfo(name = Sys.getenv("RSCONNECT_NAME"),
3 | token = Sys.getenv("RSCONNECT_TOKEN"),
4 | secret = Sys.getenv("RSCONNECT_SECRET"))
5 |
6 | # prep the test data files
7 | tmpdir <- tempdir()
8 | tmpdata <- file.path(tmpdir, "data")
9 | dir.create(tmpdata)
10 | tmpinputs <- file.path(tmpdir, "inputs")
11 | dir.create(tmpinputs, showWarnings = FALSE)
12 |
13 | data("mc_simulation_results", package = "evaluator", envir = environment())
14 | saveRDS(mc_simulation_results, file = file.path(tmpdata, "simulation_results.rds"))
15 |
16 | res <- c("risk_tolerances.csv", "domains.csv") %>%
17 | purrr::map(~ file.copy(system.file("extdata", .x, package = "evaluator"),
18 | tmpinputs))
19 |
20 | data("mc_quantitative_scenarios", envir = environment())
21 | saveRDS(mc_quantitative_scenarios, file.path(tmpinputs, "quantitative_scenarios.rds"))
22 |
23 | deployApp(here::here("inst/explore_scenarios"), appName = "scenario_explorer",
24 | appTitle = "Scenario Explorer")
25 |
--------------------------------------------------------------------------------
/man/get_mean_control_strength.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{get_mean_control_strength}
4 | \alias{get_mean_control_strength}
5 | \title{Calculate difficulty strength across multiple controls by taking the mean}
6 | \usage{
7 | get_mean_control_strength(n, diff_parameters)
8 | }
9 | \arguments{
10 | \item{n}{Number of threat events to generate control effectiveness samples.}
11 |
12 | \item{diff_parameters}{Parameters to pass to \code{\link{sample_diff}}.}
13 | }
14 | \value{
15 | Vector of control effectiveness.
16 | }
17 | \description{
18 | Given a set of estimation parameters, calculate control strength as the
19 | arithmetic mean of sampled control effectiveness.
20 | }
21 | \seealso{
22 | Other OpenFAIR helpers:
23 | \code{\link{compare_tef_vuln}()},
24 | \code{\link{openfair_tef_tc_diff_lm}()},
25 | \code{\link{sample_diff}()},
26 | \code{\link{sample_lef}()},
27 | \code{\link{sample_lm}()},
28 | \code{\link{sample_tc}()},
29 | \code{\link{sample_vuln}()},
30 | \code{\link{select_loss_opportunities}()}
31 | }
32 | \concept{OpenFAIR helpers}
33 |
--------------------------------------------------------------------------------
/man/encode_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/encode.R
3 | \name{encode_scenarios}
4 | \alias{encode_scenarios}
5 | \title{Encode qualitative data to quantitative parameters}
6 | \usage{
7 | encode_scenarios(scenarios, capabilities, mappings)
8 | }
9 | \arguments{
10 | \item{scenarios}{Qualitative risk scenarios dataframe.}
11 |
12 | \item{capabilities}{Qualitative program capabilities dataframe.}
13 |
14 | \item{mappings}{Qualitative to quantitative mapping dataframe.}
15 | }
16 | \value{
17 | A dataframe of capabilities for the scenario and parameters for quantified simulation.
18 | }
19 | \description{
20 | Given an input of:
21 | \itemize{
22 | \item qualitative risk scenarios
23 | \item qualitative capabilities
24 | \item translation table from qualitative labels to quantitative parameters
25 | }
26 | }
27 | \details{
28 | Create a unified dataframe of quantitative scenarios ready for simulation.
29 | }
30 | \examples{
31 | data(mc_qualitative_scenarios, mc_capabilities, mc_mappings)
32 | encode_scenarios(mc_qualitative_scenarios, mc_capabilities, mc_mappings)
33 | }
34 |
--------------------------------------------------------------------------------
/man/validate_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/validate.R
3 | \name{validate_scenarios}
4 | \alias{validate_scenarios}
5 | \title{Validate qualitative scenario data}
6 | \usage{
7 | validate_scenarios(scenarios, capabilities, domains, mappings)
8 | }
9 | \arguments{
10 | \item{scenarios}{Dataframe of qualitative scenarios.}
11 |
12 | \item{capabilities}{Dataframe of capabilities.}
13 |
14 | \item{domains}{Dataframe of domain mappings.}
15 |
16 | \item{mappings}{Dataframe of qualitative to quantitative mappings.}
17 | }
18 | \value{
19 | An invisible boolean as to success/failure of validation steps.
20 | }
21 | \description{
22 | Run a set of basic consistency checks on the key qualitative data inputs
23 | (scenarios, capabilities, domains, and mappings).
24 | }
25 | \details{
26 | Checks that:
27 | \itemize{
28 | \item All scenarios are distinct
29 | \item All controls referenced in scenarios are defined in the controls table
30 | \item All controls are distinct
31 | }
32 | }
33 | \examples{
34 | \dontrun{
35 | validate_scenarios(scenarios, capabilities, domains, mappings)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/testthat/test-test-tidyrisk-factor.R:
--------------------------------------------------------------------------------
1 | context("Tidyrisk Factor class")
2 |
3 | test_that("Element object can be created", {
4 | element <- tidyrisk_factor(NA, factor_label = "TF")
5 | expect_s3_class(element, "tidyrisk_factor")
6 |
7 | })
8 | test_that("Element object does not accept invalid OpenFAIR types", {
9 | expect_error(tidyrisk_factor(NA, factor_label = "ZZGO"))
10 | })
11 |
12 | test_that("Element object summary functions without samples", {
13 | element <- tidyrisk_factor(NA, factor_label = "LM")
14 | expect_is(summary(element), "list")
15 | })
16 | test_that("Element object summary functions with samples", {
17 | element <- tidyrisk_factor(c(1, 100), factor_label = "LM")
18 | expect_is(summary(element), "list")
19 | })
20 | test_that("Element object summary functions on non LM types", {
21 | element <- tidyrisk_factor(c(1, 100), factor_label = "TC")
22 | expect_is(summary(element), "list")
23 | })
24 |
25 | test_that("Factors can be logically compared", {
26 | tc_element <- tidyrisk_factor(c(10, 40, 100), factor_label = "TC")
27 | diff_element <- tidyrisk_factor(c(10, 30, 100), factor_label = "DIFF")
28 | })
29 |
--------------------------------------------------------------------------------
/man/mc_quantitative_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_quantitative_scenarios}
5 | \alias{mc_quantitative_scenarios}
6 | \title{Quantified information risk scenarios}
7 | \format{
8 | A dataset of quantified risk scenarios, with parameters
9 | describing the distribution of each input.
10 |
11 | \describe{
12 | \item{scenario_id}{id of the scenario, primary key}
13 | \item{scenario_description}{full text description of the risk scenario}
14 | \item{tcomm}{description of the threat community}
15 | \item{domain_id}{domain id}
16 | \item{control_descriptons}{named list of the text description of controls involved}
17 | \item{scenario}{\code{\link{tidyrisk_scenario}} objects}
18 | }
19 | }
20 | \source{
21 | This is hypothetical information. Any similarity to any other
22 | entity is completely coincidental.
23 | }
24 | \usage{
25 | mc_quantitative_scenarios
26 | }
27 | \description{
28 | A sample set of quantified information security risk scenarios for the
29 | demonstration (and artificial) MetroCare information security program.
30 | }
31 | \keyword{datasets}
32 |
--------------------------------------------------------------------------------
/man/sample_diff.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{sample_diff}
4 | \alias{sample_diff}
5 | \title{Calculate the difficulty presented by controls, given a function and
6 | parameters for that function}
7 | \usage{
8 | sample_diff(n, .func = NULL, params = NULL)
9 | }
10 | \arguments{
11 | \item{n}{Number of samples to generate.}
12 |
13 | \item{.func}{Function to use to simulate DIFF, defaults to \code{\link[mc2d]{rpert}}.}
14 |
15 | \item{params}{Optional parameters to pass to \code{.func}.}
16 | }
17 | \value{
18 | List containing type ("diff"), samples (as a vector), and details (as a list).
19 | }
20 | \description{
21 | Calculate the difficulty presented by controls, given a function and
22 | parameters for that function
23 | }
24 | \seealso{
25 | Other OpenFAIR helpers:
26 | \code{\link{compare_tef_vuln}()},
27 | \code{\link{get_mean_control_strength}()},
28 | \code{\link{openfair_tef_tc_diff_lm}()},
29 | \code{\link{sample_lef}()},
30 | \code{\link{sample_lm}()},
31 | \code{\link{sample_tc}()},
32 | \code{\link{sample_vuln}()},
33 | \code{\link{select_loss_opportunities}()}
34 | }
35 | \concept{OpenFAIR helpers}
36 |
--------------------------------------------------------------------------------
/man/evaluator-deprecated.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R, R/evaluator-deprecated.R,
3 | % R/load_data.R
4 | \name{generate_scatterplot}
5 | \alias{generate_scatterplot}
6 | \alias{evaluator-deprecated}
7 | \alias{load_data}
8 | \title{Deprecated functions in package \pkg{evaluator}.}
9 | \usage{
10 | generate_scatterplot(simulation_results, scenario_id)
11 |
12 | load_data(
13 | input_directory = "~/evaluator/inputs",
14 | results_directory = "~/evaluator/results"
15 | )
16 | }
17 | \description{
18 | The functions listed below are deprecated and will be defunct in
19 | the near future. When possible, alternative functions with similar
20 | functionality are also mentioned. Help pages for deprecated functions are
21 | available at \code{help("-deprecated")}.
22 | }
23 | \section{\code{generate_scatterplot}}{
24 |
25 | Instead of \code{generate_scatterplot}, use \code{\link{loss_scatterplot}}.
26 | }
27 |
28 | \section{\code{load_data}}{
29 |
30 | Instead of \code{load_data}, use \code{\link{read_quantitative_inputs}}
31 | and \code{\link{read_qualitative_inputs}} as appropriate.
32 | }
33 |
34 | \keyword{internal}
35 |
--------------------------------------------------------------------------------
/man/generate_event_outcomes_plot.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{generate_event_outcomes_plot}
4 | \alias{generate_event_outcomes_plot}
5 | \title{Display the distribution of threat events contained vs. realized across
6 | all domains}
7 | \usage{
8 | generate_event_outcomes_plot(domain_summary, domain_id = domain_id)
9 | }
10 | \arguments{
11 | \item{domain_summary}{Domain-level summary from \code{domain_summary}.}
12 |
13 | \item{domain_id}{Variable to group plot by.}
14 | }
15 | \value{
16 | A ggplot object.
17 | }
18 | \description{
19 | Creates a barbell plot showing the number and percentage of events
20 | contained (not resulting in loss) vs the number and percentage of
21 | loss events (threat events resulting in losses).
22 | }
23 | \examples{
24 | data(mc_domain_summary)
25 | generate_event_outcomes_plot(mc_domain_summary)
26 | }
27 | \seealso{
28 | Other result graphs:
29 | \code{\link{exposure_histogram}()},
30 | \code{\link{generate_heatmap}()},
31 | \code{\link{generate_scatterplot-deprecated}},
32 | \code{\link{loss_exceedance_curve}()},
33 | \code{\link{loss_scatterplot}()}
34 | }
35 | \concept{result graphs}
36 |
--------------------------------------------------------------------------------
/man/mc_qualitative_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_qualitative_scenarios}
5 | \alias{mc_qualitative_scenarios}
6 | \title{Qualitative information security risk scenarios}
7 | \format{
8 | \describe{
9 | \item{scenario_id}{id of the scenario, primary key}
10 | \item{scenario}{full text description of the risk scenario}
11 | \item{tcomm}{full text name of threat community}
12 | \item{tef}{qualitative label of threat frequency}
13 | \item{tc}{qualitative label of threat capability}
14 | \item{lm}{qualitative label of loss magnitude}
15 | \item{domain_id}{domain id}
16 | \item{controls}{comma delimited list of controls ids}
17 | }
18 | }
19 | \source{
20 | This is hypothetical information. Any similarity to any other
21 | entity is completely coincidental.
22 | }
23 | \usage{
24 | mc_qualitative_scenarios
25 | }
26 | \description{
27 | A sample set of qualitative information security risk scenarios for the
28 | demonstration (and artificial) MetroCare information security program.
29 | }
30 | \details{
31 | No connection with any other similarly named entity is intended or implied.
32 | }
33 | \keyword{datasets}
34 |
--------------------------------------------------------------------------------
/man/calculate_max_losses.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/utils.R
3 | \name{calculate_max_losses}
4 | \alias{calculate_max_losses}
5 | \title{Calculate maximum losses}
6 | \usage{
7 | calculate_max_losses(simulation_results, scenario_outliers = NULL)
8 | }
9 | \arguments{
10 | \item{simulation_results}{Simulation results dataframe.}
11 |
12 | \item{scenario_outliers}{Optional vector of IDs of outlier scenarios.}
13 | }
14 | \value{
15 | A dataframe with the following columns:
16 | \itemize{
17 | \item \code{iteration} - index of the iteration
18 | \item \code{biggest_single_scenario_loss} - the biggest annual loss in that iteration,
19 | \item \code{min_loss} - the smallest annual loss in that iteration,
20 | \item \code{max_loss} - the total annual losses in that iteration
21 | \item \code{outliers} - logical of whether or not outliers are included
22 | }
23 | }
24 | \description{
25 | Calculate the biggest single annual loss for each scenario, as well as
26 | the minimum and maximum ALE across all iterations. Calculations both
27 | with and without outliers (if passed) are returned.
28 | }
29 | \examples{
30 | data(mc_simulation_results)
31 | calculate_max_losses(mc_simulation_results)
32 | }
33 |
--------------------------------------------------------------------------------
/man/compare_tef_vuln.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{compare_tef_vuln}
4 | \alias{compare_tef_vuln}
5 | \title{Calculate number of loss events which occur in a simulated period}
6 | \usage{
7 | compare_tef_vuln(tef, vuln, n = NULL)
8 | }
9 | \arguments{
10 | \item{tef}{Threat event frequency (n).}
11 |
12 | \item{vuln}{Vulnerability (percentage).}
13 |
14 | \item{n}{Number of samples to generate.}
15 | }
16 | \value{
17 | List containing samples (as a vector) and details (as a list).
18 | }
19 | \description{
20 | Composition function for use in \code{\link{sample_lef}}. Given a count of
21 | the number of threat events (TEF) and the level of vulnerability (as a
22 | percentage), calculate how many of those become loss events (LEF).
23 | }
24 | \examples{
25 | compare_tef_vuln(tef = 500, vuln = .25)
26 | }
27 | \seealso{
28 | Other OpenFAIR helpers:
29 | \code{\link{get_mean_control_strength}()},
30 | \code{\link{openfair_tef_tc_diff_lm}()},
31 | \code{\link{sample_diff}()},
32 | \code{\link{sample_lef}()},
33 | \code{\link{sample_lm}()},
34 | \code{\link{sample_tc}()},
35 | \code{\link{sample_vuln}()},
36 | \code{\link{select_loss_opportunities}()}
37 | }
38 | \concept{OpenFAIR helpers}
39 |
--------------------------------------------------------------------------------
/tests/testthat/test-utils.R:
--------------------------------------------------------------------------------
1 | context("Utilities")
2 | test_that("Missing packages are detected", {
3 | expect_error(check_availability(packages = c("ggplot2", "Missing"), func = "test"),
4 | "available: Missing")
5 | })
6 | test_that("Found packages are detected silently", {
7 | expect_silent(check_availability(packages = c("stats"), func = "test"))
8 | })
9 |
10 | test_that("Dollar Millions formats as expected", {
11 | expect_equal(dollar_millions(1.523 * 10^6), "$1.52M")
12 | })
13 |
14 | test_that("Calculate max losses", {
15 | data("mc_simulation_results")
16 | dat <- calculate_max_losses(mc_simulation_results, c(1, 10))
17 | expect_s3_class(dat, "data.frame")
18 | expect_equal(nrow(dat[dat$outliers == TRUE, ]), 1000)
19 | expect_equal(nrow(dat), 2000)
20 | })
21 |
22 | test_that("calculate_max_losses handles NULL outliers", {
23 | data("mc_simulation_results")
24 | dat <- calculate_max_losses(mc_simulation_results)
25 | expect_s3_class(dat, "data.frame")
26 | expect_equal(nrow(dat), 1000)
27 | })
28 |
29 | test_that("identify_outliers() identifies the expected number of outliers", {
30 | data("mc_scenario_summary")
31 | dat <- identify_outliers(mc_scenario_summary)
32 | expect_equal(sum(dat$outlier), 4)
33 | })
34 |
--------------------------------------------------------------------------------
/man/derive_controls.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/encode.R
3 | \name{derive_controls}
4 | \alias{derive_controls}
5 | \title{Derive control difficulty parameters for a given qualitative scenario}
6 | \usage{
7 | derive_controls(capability_ids, capabilities, mappings)
8 | }
9 | \arguments{
10 | \item{capability_ids}{Comma-delimited list of capabilities in scope for a scenario.}
11 |
12 | \item{capabilities}{Dataframe of master list of all qualitative capabilities.}
13 |
14 | \item{mappings}{Qualitative mappings dataframe.}
15 | }
16 | \value{
17 | A named list of quantitative estimate parameters for the capabilities
18 | applicable to a given scenario.
19 | }
20 | \description{
21 | Given a comma-separated list of control IDs in a scenario, identify
22 | the qualitative rankings associated with each scenario, convert to
23 | their quantitative parameters, and return a dataframe of the set of
24 | parameters.
25 | }
26 | \examples{
27 | data(mc_capabilities)
28 | capability_ids <- c("1, 3")
29 | mappings <- data.frame(type = "diff", label = "1 - Immature", l = 0, ml = 2, h = 10,
30 | conf = 3, stringsAsFactors = FALSE)
31 | derive_controls(capability_ids, mc_capabilities, mappings)
32 | }
33 |
--------------------------------------------------------------------------------
/man/exposure_histogram.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{exposure_histogram}
4 | \alias{exposure_histogram}
5 | \title{Display a histogram of losses for a scenario}
6 | \usage{
7 | exposure_histogram(simulation_result, bins = 30, show_var_95 = FALSE)
8 | }
9 | \arguments{
10 | \item{simulation_result}{Simulation result from \code{run_simulation}.}
11 |
12 | \item{bins}{Number of bins to use for the histogram.}
13 |
14 | \item{show_var_95}{Set to TRUE to show the 95 percentile value at risk line.}
15 | }
16 | \value{
17 | A ggplot object.
18 | }
19 | \description{
20 | Given a results dataframe for a specific scenario, create a histogram of
21 | the annualized loss exposure. This provides a detailed view on the results
22 | for a particular scenario.
23 | }
24 | \examples{
25 | data(mc_simulation_results)
26 | result <- mc_simulation_results$results[[1]]
27 | exposure_histogram(result)
28 | }
29 | \seealso{
30 | Other result graphs:
31 | \code{\link{generate_event_outcomes_plot}()},
32 | \code{\link{generate_heatmap}()},
33 | \code{\link{generate_scatterplot-deprecated}},
34 | \code{\link{loss_exceedance_curve}()},
35 | \code{\link{loss_scatterplot}()}
36 | }
37 | \concept{result graphs}
38 |
--------------------------------------------------------------------------------
/man/generate_scatterplot-deprecated.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/common_graphs.R
3 | \name{generate_scatterplot-deprecated}
4 | \alias{generate_scatterplot-deprecated}
5 | \title{Display a scatterplot for a particular scenario ID}
6 | \arguments{
7 | \item{simulation_results}{Simulation results from \code{run_simulations}.}
8 |
9 | \item{scenario_id}{ID of the scenario to display.}
10 | }
11 | \value{
12 | A ggplot object.
13 | }
14 | \description{
15 | Given a detailed results dataframe and a specific scenario identifier,
16 | create a scatterplot of the number of loss events versus the total amount of
17 | expected annual loss expected for each simulation. This provides a
18 | detailed view on the results for a particular scenario.
19 | }
20 | \examples{
21 | \dontrun{
22 | data(mc_simulation_results)
23 | generate_scatterplot(mc_simulation_results, scenario_id = "RS-50")
24 | }
25 | }
26 | \seealso{
27 | \code{\link{evaluator-deprecated}}
28 |
29 | Other result graphs:
30 | \code{\link{exposure_histogram}()},
31 | \code{\link{generate_event_outcomes_plot}()},
32 | \code{\link{generate_heatmap}()},
33 | \code{\link{loss_exceedance_curve}()},
34 | \code{\link{loss_scatterplot}()}
35 | }
36 | \concept{result graphs}
37 | \keyword{internal}
38 |
--------------------------------------------------------------------------------
/.github/workflows/build-image.yaml:
--------------------------------------------------------------------------------
1 | name: Docker Image
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - main
8 |
9 | jobs:
10 | build:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout
14 | uses: actions/checkout@v2
15 | - name: Docker meta
16 | id: docker_meta
17 | uses: docker/metadata-action@v3
18 | with:
19 | images: ghcr.io/davidski/evaluator # list of Docker images to use as base name for tags
20 | tags: |
21 | type=sha
22 | - name: Set up QEMU
23 | uses: docker/setup-qemu-action@v1
24 | - name: Set up Docker Buildx
25 | uses: docker/setup-buildx-action@v1
26 | - name: Login to GitHub Container Registry
27 | uses: docker/login-action@v1
28 | with:
29 | registry: ghcr.io
30 | username: ${{ github.repository_owner }}
31 | password: ${{ secrets.CR_PAT }}
32 | - name: Build and push
33 | id: docker_build
34 | uses: docker/build-push-action@v2
35 | with:
36 | push: ${{ github.event_name != 'pull_request' }}
37 | labels: ${{ steps.docker_meta.outputs.labels }}
38 | tags: ${{ steps.docker_meta.outputs.tags }}
39 | build-args: |
40 | arg1=value1
41 | arg2=value2
42 |
--------------------------------------------------------------------------------
/man/openfair_tef_tc_diff_plm_sr.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{openfair_tef_tc_diff_plm_sr}
4 | \alias{openfair_tef_tc_diff_plm_sr}
5 | \title{Run an OpenFAIR simulation at the TEF/TC/DIFF/PLM/SR levels}
6 | \usage{
7 | openfair_tef_tc_diff_plm_sr(tef, tc, diff, plm, sr, n = 10^4, verbose = FALSE)
8 | }
9 | \arguments{
10 | \item{tef}{Parameters for TEF simulation.}
11 |
12 | \item{tc}{Parameters for TC simulation.}
13 |
14 | \item{diff}{Parameters for DIFF simulation.}
15 |
16 | \item{plm}{Parameters for PLM simulation.}
17 |
18 | \item{sr}{Parameters for SR simulation.}
19 |
20 | \item{n}{Number of iterations to run.}
21 |
22 | \item{verbose}{Whether to print progress indicators.}
23 | }
24 | \value{
25 | Dataframe of scenario name, threat_event count, loss_event count,
26 | mean TC and DIFF exceedance, and ALE samples.
27 | }
28 | \description{
29 | Run an OpenFAIR model with parameters provided for TEF, TC, DIFF, PLM, and
30 | SR sampling. If there are multiple controls provided for the scenario, the
31 | arithmetic mean (average) is taken across samples for all controls to get
32 | the effective control strength for each threat event.
33 | }
34 | \seealso{
35 | Other OpenFAIR models:
36 | \code{\link{sample_tef}()}
37 | }
38 | \concept{OpenFAIR models}
39 |
--------------------------------------------------------------------------------
/man/summarize_iterations.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/summarize.R
3 | \name{summarize_iterations}
4 | \alias{summarize_iterations}
5 | \title{Create a summary of outcomes across all scenarios}
6 | \usage{
7 | summarize_iterations(simulation_result, ..., .key = "iteration")
8 | }
9 | \arguments{
10 | \item{simulation_result}{Results object for a single scenario.}
11 |
12 | \item{...}{Additional simulation result objects to summarize.}
13 |
14 | \item{.key}{Iteration ID field}
15 | }
16 | \value{
17 | Dataframe.
18 | }
19 | \description{
20 | Given a dataframe of raw results from \code{\link{run_simulations}}, summarize
21 | the individual results at a per-iteration level.
22 | }
23 | \details{
24 | Summary stats created include:
25 | * Mean/Min/Max/Median are calculated for loss events
26 | * Median/Max/VaR are calculated for annual loss expected (ALE)
27 | * Mean/Median/Max/Min are calculated for single loss expected (SLE)
28 | * Mean percentage of threat capability exceeding difficulty on successful threat events
29 | * Mean percentage of difficulty exceeding threat capability on defended events
30 | * Vulnerability percentage
31 | * Z-score of ALE (outliers flagged as 2 >= z-score)
32 | }
33 | \examples{
34 | data(mc_simulation_results)
35 | summarize_iterations(mc_simulation_results$results)
36 | }
37 |
--------------------------------------------------------------------------------
/man/risk_dashboard.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/report.R
3 | \name{risk_dashboard}
4 | \alias{risk_dashboard}
5 | \title{Launch a single page summary risk dashboard}
6 | \usage{
7 | risk_dashboard(
8 | input_directory = "~/evaluator/inputs",
9 | results_directory = "~/evaluator/results",
10 | output_file,
11 | intermediates_dir = tempdir(),
12 | quiet = TRUE,
13 | ...
14 | )
15 | }
16 | \arguments{
17 | \item{input_directory}{Location of input files read by \code{\link{read_quantitative_inputs}}.}
18 |
19 | \item{results_directory}{Directory where the \code{simulation_results.rds} file is located.}
20 |
21 | \item{output_file}{Full path to the desired output file.}
22 |
23 | \item{intermediates_dir}{Location for intermediate knit files.}
24 |
25 | \item{quiet}{\code{TRUE} to suppress printing of pandoc output.}
26 |
27 | \item{...}{Any other parameters to pass to \code{rmarkdown::render}}
28 | }
29 | \value{
30 | Default return values of the \code{rmarkdown::render} function.
31 | }
32 | \description{
33 | Given the input files and the analysis summary file, create a basic one-
34 | page summary with an overview of the results per domain and scenario.
35 | Intended as a skeleton showing how the results could be displayed at an
36 | executive level.
37 | }
38 | \examples{
39 | \dontrun{
40 | risk_dashboard("~/inputs", "~/simulations")
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/man/select_loss_opportunities.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{select_loss_opportunities}
4 | \alias{select_loss_opportunities}
5 | \title{Determine which threat events result in loss opportunities}
6 | \usage{
7 | select_loss_opportunities(tc, diff, n = NULL, ...)
8 | }
9 | \arguments{
10 | \item{tc}{Threat capability (as a percentage).}
11 |
12 | \item{diff}{Difficulty (as a percentage).}
13 |
14 | \item{n}{Number of samples to generate.}
15 |
16 | \item{...}{Optional parameters (currently ignored).}
17 | }
18 | \value{
19 | List containing boolean values of length TC (as a vector) and details (as a list).
20 | }
21 | \description{
22 | Composition function for use in \code{\link{sample_vuln}}, does a simple
23 | compare of all threat events where the threat capability (TC) is greater
24 | than the difficulty (DIFF).
25 | }
26 | \examples{
27 | threat_capabilities <- c(.1, .5, .9)
28 | difficulties <- c(.09, .6, .8)
29 | select_loss_opportunities(threat_capabilities, difficulties)
30 | }
31 | \seealso{
32 | Other OpenFAIR helpers:
33 | \code{\link{compare_tef_vuln}()},
34 | \code{\link{get_mean_control_strength}()},
35 | \code{\link{openfair_tef_tc_diff_lm}()},
36 | \code{\link{sample_diff}()},
37 | \code{\link{sample_lef}()},
38 | \code{\link{sample_lm}()},
39 | \code{\link{sample_tc}()},
40 | \code{\link{sample_vuln}()}
41 | }
42 | \concept{OpenFAIR helpers}
43 |
--------------------------------------------------------------------------------
/man/summarize_domains.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/summarize.R
3 | \name{summarize_domains}
4 | \alias{summarize_domains}
5 | \title{Create domain-level summary of simulation results}
6 | \usage{
7 | summarize_domains(simulation_results, domain_variable = "domain_id")
8 | }
9 | \arguments{
10 | \item{simulation_results}{Simulation results dataframe.}
11 |
12 | \item{domain_variable}{Variable by which individual simulations should be grouped.}
13 | }
14 | \value{
15 | Simulation results summarized across domains.
16 | }
17 | \description{
18 | Given a dataframe of raw results from \code{\link{run_simulations}}, summarize
19 | the individual results at a per-domain level. This domain-level summary
20 | is a useful data structure for aggregate reporting.
21 | }
22 | \details{
23 | Summary stats created include:
24 | \itemize{
25 | \item Mean/Min/Max/Median are calculated for loss events
26 | \item Median/Max/VaR are calculated for annual loss expected (ALE)
27 | \item Mean/Median/Max/Min are calculated for single loss expected (SLE)
28 | \item Mean percentage of threat capability exceeding difficulty on successful threat events
29 | \item Mean percentage of difficulty exceeding threat capability on defended events
30 | \item Vulnerability percentage
31 | }
32 | }
33 | \examples{
34 | \dontrun{
35 | data(mc_simulation_results)
36 | summarize_domains(mc_simulation_results)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/man/run_simulations.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/simulate.R
3 | \name{run_simulations}
4 | \alias{run_simulations}
5 | \title{Run simulations for a list of scenarios}
6 | \usage{
7 | run_simulations(
8 | scenario,
9 | ...,
10 | iterations = 10000L,
11 | ale_maximum = NULL,
12 | verbose = FALSE,
13 | simulation_count = NULL
14 | )
15 | }
16 | \arguments{
17 | \item{scenario}{A \link{tidyrisk_scenario} object.}
18 |
19 | \item{...}{Additional \code{tidyrisk_scenario} objects to simulate.}
20 |
21 | \item{iterations}{Number of iterations to run on each scenario.}
22 |
23 | \item{ale_maximum}{Maximum practical annual losses.}
24 |
25 | \item{verbose}{Whether verbose console output is requested.}
26 |
27 | \item{simulation_count}{\strong{DEPRECATED} Number of simulations to perform.}
28 | }
29 | \value{
30 | A list of one dataframe of results for each scenario.
31 | }
32 | \description{
33 | Given a list of quantitative scenario objects of type \code{tidyrisk_scenario},
34 | run a OpenFAIR Monte Carlo simulation for each scenario.
35 | }
36 | \examples{
37 | # fetch three scenarios for this example
38 | data(mc_quantitative_scenarios)
39 | scenario_a <- mc_quantitative_scenarios$scenario[[1]]
40 | scenario_b <- mc_quantitative_scenarios$scenario[[2]]
41 | scenario_c <- mc_quantitative_scenarios$scenario[[3]]
42 | run_simulations(scenario_a, scenario_b, scenario_c, iterations = 10)
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/.github/workflows/test-coverage.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 | - master
6 | pull_request:
7 | branches:
8 | - main
9 | - master
10 |
11 | name: test-coverage
12 |
13 | jobs:
14 | test-coverage:
15 | runs-on: macOS-latest
16 | env:
17 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
18 | steps:
19 | - uses: actions/checkout@v2
20 |
21 | - uses: r-lib/actions/setup-r@v1
22 |
23 | - uses: r-lib/actions/setup-pandoc@v1
24 |
25 | - name: Query dependencies
26 | run: |
27 | install.packages('remotes')
28 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
29 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
30 | shell: Rscript {0}
31 |
32 | - name: Cache R packages
33 | uses: actions/cache@v2
34 | with:
35 | path: ${{ env.R_LIBS_USER }}
36 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
37 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
38 |
39 | - name: Install dependencies
40 | run: |
41 | install.packages(c("remotes"))
42 | remotes::install_deps(dependencies = TRUE)
43 | remotes::install_cran("covr")
44 | shell: Rscript {0}
45 |
46 | - name: Test coverage
47 | run: covr::codecov()
48 | shell: Rscript {0}
49 |
--------------------------------------------------------------------------------
/man/mc_domain_summary.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_domain_summary}
5 | \alias{mc_domain_summary}
6 | \title{Domain-level risk summary}
7 | \format{
8 | \describe{
9 | \item{domain_id}{abbreviated name of the domain}
10 | \item{loss_events_mean}{mean number of loss events}
11 | \item{loss_events_min}{minimum number of loss events}
12 | \item{loss_events_max}{maximum number of loss events}
13 | \item{loss_events_median}{median number of loss events}
14 | \item{ale_max}{minimum annual loss expected}
15 | \item{ale_median}{median annual loss expected}
16 | \item{ale_mean}{mean annual loss expected}
17 | \item{ale_max}{maximum annual loss expected}
18 | \item{ale_sd}{standard deviation annual loss expected}
19 | \item{ale_var}{value at risk, ale}
20 | \item{mean_threat_events}{mean threat events}
21 | \item{mean_avoided_events}{mean avoided events}
22 | \item{mean_tc_exceedance}{mean threat capability exceedance}
23 | \item{mean_diff_exceedance}{mean difficulty exceedance}
24 | \item{mean_vuln}{mean vulnerability of the scenario}
25 | }
26 | }
27 | \source{
28 | This is hypothetical information. Any similarity to any other
29 | entity is completely coincidental.
30 | }
31 | \usage{
32 | mc_domain_summary
33 | }
34 | \description{
35 | A sample set of quantified information security risk exposure, summarized
36 | at the domain level, for the demonstration (and artificial)
37 | MetroCare information security program.
38 | }
39 | \keyword{datasets}
40 |
--------------------------------------------------------------------------------
/DESCRIPTION:
--------------------------------------------------------------------------------
1 | Package: evaluator
2 | Title: Quantified Risk Assessment Toolkit
3 | Version: 0.4.4
4 | Authors@R: c(person("David", "Severski", email = "davidski@deadheaven.com", role = c("aut", "cre"), comment = c(ORCID = "0000-0001-7867-0459")))
5 | Description: An open source risk analysis toolkit based on the OpenFAIR ontology
6 | and risk analysis standard
7 | . Empowers an organization to
8 | perform a quantifiable, repeatable, and data-driven risk review.
9 | Depends: R (>= 3.3.0)
10 | License: MIT + file LICENSE
11 | Encoding: UTF-8
12 | LazyData: true
13 | Imports:
14 | cli,
15 | dplyr,
16 | ggplot2,
17 | mc2d,
18 | magrittr,
19 | purrr,
20 | readr,
21 | readxl,
22 | rlang,
23 | rstudioapi,
24 | scales,
25 | stringi,
26 | tibble,
27 | tidyr,
28 | vctrs,
29 | viridis
30 | Roxygen: list(markdown = TRUE)
31 | RoxygenNote: 7.1.2
32 | Suggests:
33 | DT,
34 | EnvStats,
35 | covr,
36 | knitr,
37 | flexdashboard (>= 0.4),
38 | forcats,
39 | furrr,
40 | markdown,
41 | mockery,
42 | pander (>= 0.6.1),
43 | psych,
44 | rmarkdown (>= 1.9),
45 | shiny,
46 | shinytest,
47 | spelling,
48 | statip,
49 | sysfonts,
50 | testthat
51 | SystemRequirements: pandoc
52 | VignetteBuilder: knitr
53 | Config/testthat/edition: 3
54 | URL: https://evaluator.tidyrisk.org
55 | BugReports: https://github.com/davidski/evaluator/issues
56 | Language: en-US
57 |
--------------------------------------------------------------------------------
/tests/testthat/test-explore-scenarios.R:
--------------------------------------------------------------------------------
1 | context("Scenario Explorer")
2 | # This file is for testing the applications in the apps/ directory.
3 |
4 | library(shinytest)
5 |
6 |
7 | test_that("explore_scenarios() works", {
8 | # Don't run these tests on the CRAN build servers
9 | skip_on_cran()
10 |
11 | # unsure how to pass parameters to a shiny flexdashboard
12 | skip("Shinytest scaffolding is not complete")
13 |
14 | # prep the test data files
15 | tmpdir <- tempdir()
16 | tmpdata <- file.path(tmpdir, "data")
17 | dir.create(tmpdata)
18 | tmpinputs <- file.path(tmpdir, "inputs")
19 | dir.create(tmpinputs, showWarnings = FALSE)
20 |
21 | data("mc_simulation_results", package = "evaluator", envir = environment())
22 | saveRDS(mc_simulation_results, file = file.path(tmpdata, "simulation_results.rds"))
23 |
24 | res <- c("risk_tolerances.csv") %>%
25 | purrr::map(~ file.copy(system.file("extdata", .x, package = "evaluator"),
26 | tmpinputs))
27 |
28 | data("mc_quantitative_scenarios", envir = environment())
29 | saveRDS(mc_quantitative_scenarios, file.path(tmpinputs, "quantitative_scenarios.rds"))
30 |
31 | # Use compareImages=FALSE because the expected image screenshots were created
32 | # on a Mac, and they will differ from screenshots taken on the CI platform,
33 | # which runs on Linux.
34 | appdir <- system.file(package = "evaluator", "explore_scenarios")
35 | expect_pass(testApp(appdir, compareImages = FALSE))
36 | unlink(c(tmpdata, tmpinputs), recursive = TRUE)
37 | })
38 |
39 |
--------------------------------------------------------------------------------
/tests/testthat/test-import.R:
--------------------------------------------------------------------------------
1 | context("Spreadsheet imports")
2 | test_that("Default scenarios import", {
3 | data(mc_domains)
4 | scenarios <- import_scenarios(domains = mc_domains)
5 | expect_equal(nrow(scenarios), 56)
6 | expect_equal(length(scenarios), 8)
7 | })
8 | test_that("Scenarios import succeeds when using defaults", {
9 | data(mc_qualitative_scenarios)
10 | expect_equal(import_scenarios(), mc_qualitative_scenarios)
11 | })
12 | test_that("Scenarios import fails when given bad input file", {
13 | expect_error(import_scenarios(survey_file = "/bad/nonexistant/file", "survey"))
14 | })
15 |
16 | test_that("Default capabilities import", {
17 | data(mc_domains)
18 | dat <- import_capabilities(domains = mc_domains)
19 | expect_equal(nrow(dat), 60)
20 | expect_equal(length(dat), 4)
21 | })
22 | test_that("Capabilities import fails when given bad input file", {
23 | expect_error(import_capabilities(survey_file = "/bad/nonexistant/file", "survey"))
24 | })
25 | test_that("Capabilities import succeeds when using defaults", {
26 | data("mc_capabilities", envir = .GlobalEnv)
27 | src <- get("mc_capabilities", envir = .GlobalEnv)
28 | dat <- import_capabilities()
29 | expect_equal(dat, src)
30 | })
31 |
32 | test_that("Higher-level import_spreadsheet functions", {
33 | dat <- import_spreadsheet(output_dir = tempdir())
34 | expect_equal(nrow(dat), 2)
35 | })
36 | test_that("Spreadsheet import fails when given bad output directory", {
37 | expect_error(import_spreadsheet(output_dir = "/bad/nonexistant/path", "output"))
38 | })
39 |
--------------------------------------------------------------------------------
/man/explore_scenarios.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/report.R
3 | \name{explore_scenarios}
4 | \alias{explore_scenarios}
5 | \title{Launch the Scenario Explorer web application}
6 | \usage{
7 | explore_scenarios(
8 | input_directory = "~/evaluator/inputs",
9 | results_directory = "~/evaluator/results",
10 | styles = NULL,
11 | intermediates_dir = tempdir(),
12 | quiet = TRUE,
13 | ...
14 | )
15 | }
16 | \arguments{
17 | \item{input_directory}{Location of input files to be read by \code{\link{read_quantitative_inputs}}.}
18 |
19 | \item{results_directory}{Directory where the \code{simulations_results.rds} file is stored.}
20 |
21 | \item{styles}{Optional full path to CSS file to override default styles.}
22 |
23 | \item{intermediates_dir}{Location for intermediate knit files.}
24 |
25 | \item{quiet}{\code{TRUE} to suppress printing of pandoc output.}
26 |
27 | \item{...}{Any other parameters to pass to \code{rmarkdown::run}.}
28 | }
29 | \value{
30 | Invisible NULL.
31 | }
32 | \description{
33 | Evaluator provides a simple Shiny-based web application for interactive
34 | exploration of simulation results. This allows a user to interactively
35 | review simulation output without generating an extensive report. For users
36 | comfortable with R, working directly with the result dataframes will usually
37 | be preferable, with the Explorer application provided as a bare-bones data
38 | exploration tool.
39 | }
40 | \examples{
41 | \dontrun{
42 | explore_scenarios("~/inputs", "~/results")
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/man/summarize_scenario.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/summarize.R
3 | \name{summarize_scenario}
4 | \alias{summarize_scenario}
5 | \alias{summarize_scenarios}
6 | \title{Create a summary of the simulation results for a single scenario}
7 | \usage{
8 | summarize_scenario(simulation_result)
9 |
10 | summarize_scenarios(simulation_results)
11 | }
12 | \arguments{
13 | \item{simulation_result}{Results object for a single scenario.}
14 |
15 | \item{simulation_results}{Simulation results dataframe.}
16 | }
17 | \value{
18 | Dataframe of summary statistics.
19 | }
20 | \description{
21 | Given a dataframe of raw results from \code{\link{run_simulations}}, create
22 | summary statistics for the scenario. This is generally the most granular
23 | level of useful data for reporting and analysis (full simulation results
24 | are rarely directly helpful).
25 | }
26 | \details{
27 | Summary stats created include:
28 | * Mean/Min/Max/Median are calculated for loss events
29 | * Median/Max/VaR are calculated for annual loss expected (ALE)
30 | * Mean/Median/Max/Min are calculated for single loss expected (SLE)
31 | * Mean percentage of threat capability exceeding difficulty on successful threat events
32 | * Mean percentage of difficulty exceeding threat capability on defended events
33 | * Vulnerability percentage
34 | }
35 | \examples{
36 | data(mc_simulation_results)
37 | # summarize a single scenario
38 | summarize_scenario(mc_simulation_results$results[[1]])
39 |
40 | # summarize all scenarios in a data frame
41 | data(mc_simulation_results)
42 | summarize_scenarios(mc_simulation_results)
43 | }
44 |
--------------------------------------------------------------------------------
/man/mc_scenario_summary.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/data.R
3 | \docType{data}
4 | \name{mc_scenario_summary}
5 | \alias{mc_scenario_summary}
6 | \title{Scenario-level risk summary}
7 | \format{
8 | \describe{
9 | \item{scenario_id}{ID of the scenario}
10 | \item{domain_id}{domain id}
11 | \item{control_description}{control description}
12 | \item{results}{nested data frame of simulation results for the scenario}
13 | \item{loss_events_mean}{mean number of loss events}
14 | \item{loss_events_median}{median number of loss events}
15 | \item{loss_events_min}{minimum number of loss events}
16 | \item{loss_events_max}{maximum number of loss events}
17 | \item{ale_median}{median annual loss expected}
18 | \item{ale_max}{maximum annual loss expected}
19 | \item{ale_var}{value at risk, ale}
20 | \item{sle_min}{minimum single loss expectancy}
21 | \item{sle_max}{maximum single loss expectancy}
22 | \item{sle_mean}{mean single loss expectancy}
23 | \item{sle_median}{median single loss expectancy}
24 | \item{mean_tc_exceedance}{mean threat capability exceedance}
25 | \item{mean_diff_exceedance}{mean difficulty exceedance}
26 | \item{mean_vuln}{mean vulnerability of the scenario}
27 | }
28 | }
29 | \source{
30 | This is hypothetical information. Any similarity to any other
31 | entity is completely coincidental.
32 | }
33 | \usage{
34 | mc_scenario_summary
35 | }
36 | \description{
37 | A sample set of quantified information security risk exposure, summarized
38 | at the scenario level, for the demonstration (and artificial)
39 | MetroCare information security program.
40 | }
41 | \keyword{datasets}
42 |
--------------------------------------------------------------------------------
/man/openfair_tef_tc_diff_lm.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/openfair.R
3 | \name{openfair_tef_tc_diff_lm}
4 | \alias{openfair_tef_tc_diff_lm}
5 | \title{Run an OpenFAIR simulation at the TEF/TC/DIFF/LM levels}
6 | \usage{
7 | openfair_tef_tc_diff_lm(tef, tc, diff, lm, n = 10^4, verbose = FALSE)
8 | }
9 | \arguments{
10 | \item{tef}{Parameters for TEF simulation}
11 |
12 | \item{tc}{Parameters for TC simulation}
13 |
14 | \item{diff}{Parameters for DIFF simulation}
15 |
16 | \item{lm}{Parameters for LM simulation}
17 |
18 | \item{n}{Number of iterations to run.}
19 |
20 | \item{verbose}{Whether to print progress indicators.}
21 | }
22 | \value{
23 | Dataframe of scenario name, threat_event count, loss_event count,
24 | mean TC and DIFF exceedance, and ALE samples.
25 | }
26 | \description{
27 | Run an OpenFAIR model with parameters provided for TEF, TC, DIFF, and
28 | LM sampling. If there are multiple controls provided for the scenario, the
29 | arithmetic mean (average) is taken across samples for all controls to get
30 | the effective control strength for each threat event.
31 | }
32 | \examples{
33 | data(mc_quantitative_scenarios)
34 | params <- mc_quantitative_scenarios$scenario[[1]]$parameters
35 | openfair_tef_tc_diff_lm(params$tef, params$tc, params$diff, params$lm, 10)
36 | }
37 | \seealso{
38 | Other OpenFAIR helpers:
39 | \code{\link{compare_tef_vuln}()},
40 | \code{\link{get_mean_control_strength}()},
41 | \code{\link{sample_diff}()},
42 | \code{\link{sample_lef}()},
43 | \code{\link{sample_lm}()},
44 | \code{\link{sample_tc}()},
45 | \code{\link{sample_vuln}()},
46 | \code{\link{select_loss_opportunities}()}
47 | }
48 | \concept{OpenFAIR helpers}
49 |
--------------------------------------------------------------------------------
/scripts/generate_sample_reports.R:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env r
2 |
3 | # ensure devools is available
4 | if (!requireNamespace("devtools", quietly = TRUE)) remotes::install_cran("devtools")
5 |
6 | # Regenerate sample reports
7 | devtools::load_all()
8 |
9 | if (!requireNamespace("here", quietly = TRUE)) remotes::install_cran("here")
10 |
11 | tmpdir <- tempdir()
12 | tmpdata <- file.path(tmpdir, "data")
13 | dir.create(tmpdata, showWarnings = FALSE)
14 | tmpinputs <- file.path(tmpdir, "inputs")
15 | dir.create(tmpinputs, showWarnings = FALSE)
16 |
17 | output_dir <- here::here("reports")
18 | dir.create(output_dir, showWarnings = FALSE)
19 |
20 | data("mc_simulation_results", package = "evaluator", envir = environment())
21 | saveRDS(mc_simulation_results, file = file.path(tmpdata, "simulation_results.rds"))
22 | data("mc_scenario_summary", package = "evaluator", envir = environment())
23 | saveRDS(mc_scenario_summary, file = file.path(tmpdata, "scenario_summary.rds"))
24 | data("mc_domain_summary", package = "evaluator", envir = environment())
25 | saveRDS(mc_domain_summary, file = file.path(tmpdata, "domain_summary.rds"))
26 |
27 | res <- c("domains.csv", "qualitative_mappings.csv", "risk_tolerances.csv") %>%
28 | purrr::map(~ file.copy(system.file("extdata", .x, package = "evaluator"),
29 | tmpinputs))
30 | data("mc_qualitative_scenarios", envir = environment())
31 | readr::write_csv(mc_qualitative_scenarios, file.path(tmpinputs, "qualitative_scenarios.csv"))
32 | data("mc_quantitative_scenarios", envir = environment())
33 | saveRDS(mc_quantitative_scenarios, file.path(tmpinputs, "quantitative_scenarios.rds"))
34 |
35 | file <- file.path(output_dir, "evaluator_risk_analysis.html")
36 |
37 | generate_report(input_directory = tmpinputs,
38 | results_directory = tmpdata,
39 | output_file = file)
40 |
41 | file <- file.path(output_dir, "evaluator_risk_dashboard.html")
42 |
43 | risk_dashboard(input_directory = tmpinputs,
44 | results_directory = tmpdata,
45 | output_file = file)
46 |
47 | unlink(c(tmpdata, tmpinputs), recursive = TRUE)
48 |
--------------------------------------------------------------------------------
/man/generate_report.Rd:
--------------------------------------------------------------------------------
1 | % Generated by roxygen2: do not edit by hand
2 | % Please edit documentation in R/report.R
3 | \name{generate_report}
4 | \alias{generate_report}
5 | \title{Generate sample analysis report}
6 | \usage{
7 | generate_report(
8 | input_directory = "~/evaluator/inputs",
9 | results_directory = "~/evaluator/results",
10 | output_file,
11 | styles = NULL,
12 | include_header = NULL,
13 | focus_scenario_ids = c("RS-51", "RS-12"),
14 | format = "html",
15 | intermediates_dir = tempdir(),
16 | quiet = TRUE,
17 | ...
18 | )
19 | }
20 | \arguments{
21 | \item{input_directory}{Location of input files.}
22 |
23 | \item{results_directory}{Location of simulation results.}
24 |
25 | \item{output_file}{Full path to output file.}
26 |
27 | \item{styles}{Optional full path to CSS file to override default styles.}
28 |
29 | \item{include_header}{Optional full path to HTML to include in the HEAD section (HTML formats only).}
30 |
31 | \item{focus_scenario_ids}{IDs of scenarios of special interest.}
32 |
33 | \item{format}{Format to generate (html, pdf, word).}
34 |
35 | \item{intermediates_dir}{Location for intermediate knit files.}
36 |
37 | \item{quiet}{\code{TRUE} to suppress printing of pandoc output.}
38 |
39 | \item{...}{Any other parameters to pass straight to \code{rmarkdown::render}.}
40 | }
41 | \value{
42 | Default return values of the \code{rmarkdown::render} function.
43 | }
44 | \description{
45 | Given a set of input files and summarized simulation results, create a
46 | skeleton risk analysis report. This report attempts to summarize the results
47 | of the analysis at a top level, using 95\% Value at Risk (VaR) as the primary
48 | metric, while also providing more detailed analysis at both a per-domain and
49 | per-scenario level.
50 | }
51 | \details{
52 | This report includes several sections where an analyst will need to modify and
53 | fill in details for their specific organization. Of particular note is the
54 | Recommendations section, which will always need to be updated.
55 | }
56 | \examples{
57 | \dontrun{
58 | generate_report("~/inputs", "~/results", "~/risk_report.html")
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/vignettes/customization.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Advanced Customization"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{Advanced Customization}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | Evaluator makes several assumptions to get you up and running as quickly as
11 | possible. Advanced users may implement several different customizations
12 | including:
13 |
14 | ## Parameters
15 |
16 | - Risk tolerances - Organizational risk tolerances at a "medium" and "high"
17 | level are defined in `inputs/risk_tolerances.csv`. Risk tolerances are the
18 | aggregate economic loss thresholds defined by your organization. These are not
19 | necessarily the same as the size of potential losses from individual
20 | scenarios. A good proxy for risk tolerance is the budget authority implemented
21 | in your organization. The size of purchase signoff required at the executive
22 | level is generally a good indicator of the minimum floor for high risk
23 | tolerance.
24 | - Qualitative mappings - The translation of qualitative labels such as
25 | "Frequent" threat events and "Optimized" controls are defined in
26 | `inputs/qualitative_mappings.csv`. The values in this mapping may be changed
27 | but they must use lowercase and agree with the values used in the survey
28 | spreadsheet. Changing the number of levels used for any qualitative label
29 | (e.g. changing High/Medium/Low to High/Medium/Low/VeryLow) is possible as long
30 | as the survey spreadsheet uses the same labels.
31 | - The survey spreadsheet uses data validation to populate drop downs. If you
32 | change the mappings, the validation will need to be updated or removed to
33 | agree with your new mappings.
34 |
35 | ## Report Customization
36 |
37 | If you would rather work on the source RMarkdown file or direct editing and
38 | graphics tweaking, use `system.file("rmd, "analyze_risk.Rmd", package = "evaluator")`
39 | to find the location of the native template on your system.
40 |
41 | - Styling - Look and feel (fonts, colors, etc.) is defined in the
42 | `styles/html-styles.css` and `styles/word-styles-reference.docx` files.
43 |
--------------------------------------------------------------------------------
/INSTALL_EXTRAS.md:
--------------------------------------------------------------------------------
1 | # Installation Extras
2 |
3 | While using the full power of Evaluator is easier with some basic proficency with the R langauge, making quantitative risk analysis available to a broader audience has always been a core goal of this project. Users with less R experience may appreciate these notes on setting up a R environment.
4 |
5 | # Docker
6 |
7 | The simplest method applicable to most users is the corresponding [evaluator-docker](https://github.com/davidski/evaluator-docker) project. This Dockerfile (and the corresponding pre-built image on [Docker Hub](https://hub.docker.com/r/davidski/evaluator-docker/)) is the fastest and surest means of getting started with Evaluator.
8 |
9 | # MacOS
10 |
11 | For Mac users running homebrew, the following terminal commands can be used to
12 | set up a functional environment.
13 |
14 | ```
15 | # R and RStudio under homebrew derived from @hrbrmstr's post
16 | # https://rud.is/b/2015/10/22/installing-r-on-os-x-100-homebrew-edition/
17 |
18 | # Core R and RStudio
19 | brew install homebrew/science/R
20 | brew install Caskroom/cask/rstudio
21 |
22 | # Latex required for PDF knitting
23 | brew cask install mactex
24 |
25 | # extra libraries to make other packages easier to work with
26 | brew install libsvg curl libxml2 gdal geos boost
27 |
28 | # font installation
29 | brew tap caskroom/fonts
30 | brew cask install font-fira-code font-iosevka font-inconsolata font-open-sans-condensed font-open-sans font-roboto-condensed
31 | R -e 'install.package("extrafont", repos="http://cran.cnr.berkeley.edu")'
32 | R -e 'extrafont::font_import(prompt = FALSE)'
33 | ```
34 |
35 | Then install the Evaluator library either directly from CRAN via `install.packages('evaluator')` or from GitHub via `devtools::install_github('davidski/evaluator')`.
36 |
37 | # Windows
38 |
39 | Windows users should use [chocolately](https://chocolatey.org/install) for a simple `homebrew`-like package manager experience.
40 |
41 | - Install R
42 |
43 | - `choco install --yes r`
44 |
45 | - Install miktex for PDF knitting
46 |
47 | - `choco install --yes miktex`
48 |
49 | - Install RStudio (not available on Chocolately)
50 |
51 | - https://www.rstudio.com/products/rstudio/download/#download
--------------------------------------------------------------------------------
/tests/testthat/test-load-data.R:
--------------------------------------------------------------------------------
1 | context("Data Loads")
2 | tmpdir <- tempdir()
3 | tmpdata <- file.path(tmpdir, "data")
4 | dir.create(tmpdata)
5 | tmpinputs <- file.path(tmpdir, "inputs")
6 | dir.create(tmpinputs, showWarnings = FALSE)
7 |
8 | data("mc_simulation_results", package = "evaluator", envir = environment())
9 | saveRDS(mc_simulation_results, file = file.path(tmpdata, "simulation_results.rds"))
10 | data("mc_scenario_summary", package = "evaluator", envir = environment())
11 | saveRDS(mc_scenario_summary, file = file.path(tmpdata, "scenario_summary.rds"))
12 | data("mc_domain_summary", package = "evaluator", envir = environment())
13 | saveRDS(mc_domain_summary, file = file.path(tmpdata, "domain_summary.rds"))
14 |
15 | data("mc_capabilities", package = "evaluator", envir = environment())
16 | readr::write_csv(mc_capabilities, file.path(tmpinputs, "capabilities.csv"))
17 |
18 |
19 | res <- c("domains.csv", "qualitative_mappings.csv", "risk_tolerances.csv") %>%
20 | purrr::map(~ file.copy(system.file("extdata", .x, package = "evaluator"),
21 | tmpinputs))
22 | data("mc_qualitative_scenarios", envir = environment())
23 | saveRDS(mc_qualitative_scenarios, file = file.path(tmpinputs, "qualitative_scenarios.rds"))
24 | readr::write_csv(mc_qualitative_scenarios, file.path(tmpinputs, "qualitative_scenarios.csv"))
25 | data("mc_quantitative_scenarios", envir = environment())
26 | saveRDS(mc_quantitative_scenarios, file.path(tmpinputs, "quantitative_scenarios.rds"))
27 |
28 | test_that("Template files can be copied", {
29 | tmpdata <- file.path(tempdir(), "templates")
30 | dir.create(tmpdata, showWarnings = FALSE)
31 | res <- create_templates(tmpdata)
32 | expect_equal(sum(res$copied), 5)
33 | unlink(tmpdata, recursive = TRUE)
34 | })
35 |
36 | test_that("Deprecated load data function works", {
37 | expect_warning(load_data(tmpinputs, tmpdata))
38 | })
39 |
40 | test_that("Qualitative inputs can be loaded",{
41 | dat <- read_qualitative_inputs(tmpinputs)
42 | expect_is(dat, "list")
43 | })
44 |
45 | test_that("Quantitative inputs can be loaded", {
46 | dat <- read_quantitative_inputs(tmpinputs)
47 | expect_is(dat, "list")
48 | })
49 |
50 | unlink(c(tmpdata, tmpinputs), recursive = TRUE)
51 |
--------------------------------------------------------------------------------
/tests/testthat/test-common-graphs.R:
--------------------------------------------------------------------------------
1 | test_that("Basefont selection works", {
2 | dat <- get_base_fontfamily()
3 | expect_type(dat, "character")
4 | expect_gt(nchar(dat), 1)
5 | })
6 | test_that("Basefont returns sans when no fonts available", {
7 | mockery::stub(get_base_fontfamily, 'sysfonts::font_families', "")
8 | expect_equal(get_base_fontfamily(), "sans")
9 | })
10 | test_that("Basefont returns Arial Narrow when available", {
11 | mockery::stub(get_base_fontfamily, 'sysfonts::font_families', "Arial Narrow")
12 | expect_equal(get_base_fontfamily(), "Arial Narrow")
13 | })
14 |
15 |
16 | test_that("Theme functions", {
17 | gg <- theme_evaluator()
18 | expect_s3_class(gg, "gg")
19 | expect_s3_class(gg, "theme")
20 | })
21 |
22 | test_that("Domain VaR heatmap", {
23 | data(mc_domain_summary)
24 | gg <- generate_heatmap(mc_domain_summary)
25 | expect_s3_class(gg, "gg")
26 | })
27 |
28 | test_that("generate_scatterplot() throws a warning", {
29 | data(mc_simulation_results)
30 | expect_warning(generate_scatterplot(mc_simulation_results, scenario_id = "RS-50"))
31 | })
32 |
33 | test_that("loss_scatterplot() functions", {
34 | data(mc_simulation_results)
35 | mc_simulation_results %>% filter(scenario_id == "RS-50") %>%
36 | unnest(.data$results) -> my_results
37 | gg <- loss_scatterplot(my_results)
38 | expect_s3_class(gg, "gg")
39 | })
40 |
41 | test_that("Exposure histogram", {
42 | data(mc_simulation_results)
43 | gg <- mc_simulation_results %>% filter(scenario_id == "RS-50") %>%
44 | unnest(.data$results) %>% exposure_histogram()
45 | expect_s3_class(gg, "gg")
46 | })
47 |
48 | test_that("Exposure histogram with VaR line", {
49 | data(mc_simulation_results)
50 | gg <- mc_simulation_results %>% filter(scenario_id == "RS-50") %>%
51 | unnest(.data$results) %>% exposure_histogram(show_var_95 = TRUE)
52 | expect_s3_class(gg, "gg")
53 | })
54 |
55 | test_that("Domain-level outcomes", {
56 | data(mc_domain_summary)
57 | gg <- generate_event_outcomes_plot(mc_domain_summary)
58 | expect_s3_class(gg, "gg")
59 | })
60 |
61 | test_that("Loss_exceedance_curve", {
62 | data("mc_simulation_results")
63 | gg <- summarize_iterations(mc_simulation_results$results) %>%
64 | loss_exceedance_curve()
65 | expect_s3_class(gg, "gg")
66 | })
67 |
--------------------------------------------------------------------------------
/tests/testthat/test-tidyrisk-scenario.R:
--------------------------------------------------------------------------------
1 | context("Tidyrisk Scenario class")
2 | test_that("Scenario object can be created and coerced to tibble", {
3 | tidyrisk_scenario(
4 | diff_params = list(list(
5 | "2" = list(min = 70L, mode = 85, max = 98L, shape = 4L, func = "mc2d::rpert"),
6 | "5" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
7 | "7" = list(min = 20L, mode = 30, max = 50L, shape = 4L, func = "mc2d::rpert"),
8 | "32" = list(min = 20L, mode = 30, max = 50L, shape = 4L, func = "mc2d::rpert"),
9 | "14" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
10 | "15" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
11 | "16" = list(min = 0L, mode = 10, max = 30L, shape = 4L, func = "mc2d::rpert")
12 | )),
13 | tef_params = list(list(min = 10L, mode = 24, max = 52L, shape = 4L, func = "mc2d::rpert")),
14 | tc_params = list(list(min = 33L, mode = 50, max = 60L, shape = 3L, func = "mc2d::rpert")),
15 | lm_params = list(list(min = 10000L, mode = 20000, max = 500000L, shape = 4L,
16 | func = "mc2d::rpert"))
17 | ) -> scenario
18 | expect_s3_class(scenario, "tidyrisk_scenario")
19 | expect_s3_class(as_tibble(scenario), "tbl")
20 | })
21 |
22 | test_that("Unnamed parameters throw errors", {
23 | expect_error(tidyrisk_scenario(
24 | diff_params = list(list(
25 | "2" = list(min = 70L, mode = 85, max = 98L, shape = 4L, func = "mc2d::rpert"),
26 | "5" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
27 | "7" = list(min = 20L, mode = 30, max = 50L, shape = 4L, func = "mc2d::rpert"),
28 | "32" = list(min = 20L, mode = 30, max = 50L, shape = 4L, func = "mc2d::rpert"),
29 | "14" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
30 | "15" = list(min = 50L, mode = 70, max = 84L, shape = 4L, func = "mc2d::rpert"),
31 | "16" = list(min = 0L, mode = 10, max = 30L, shape = 4L, func = "mc2d::rpert")
32 | )),
33 | list(list(min = 10L, mode = 24, max = 52L, shape = 4L, func = "mc2d::rpert")),
34 | tc_params = list(list(min = 33L, mode = 50, max = 60L, shape = 3L, func = "mc2d::rpert")),
35 | lm_params = list(list(min = 10000L, mode = 20000, max = 500000L, shape = 4L,
36 | func = "mc2d::rpert"))),
37 | "unnamed")
38 | })
39 |
--------------------------------------------------------------------------------
/tests/testthat/test-simulate.R:
--------------------------------------------------------------------------------
1 | context("Simulation-Model Interface")
2 |
3 | # Simulation-Model Interface ----------------------------------------------
4 |
5 | test_that("Simulation fails when given a simulation_count", {
6 | data("mc_quantitative_scenarios")
7 | good_scen <- mc_quantitative_scenarios$scenario[[1]]
8 | expect_error(run_simulation(good_scen, simulation_count = 10L), regexp = "iteration")
9 | })
10 |
11 | test_that("Simulation fails when not given a scenario object", {
12 | data("mc_quantitative_scenarios")
13 | bad_scen <- mc_quantitative_scenarios$scenario[[1]]
14 | class(bad_scen) <- "list"
15 | expect_error(run_simulation(bad_scen, 10L), regexp = "object")
16 | })
17 |
18 | test_that("Simulation respects maximum ALE", {
19 | data("mc_quantitative_scenarios")
20 | good_scen <- mc_quantitative_scenarios$scenario[[1]]
21 | results <- run_simulation(good_scen, 10L, ale_maximum = 100)
22 | expect_lte(max(results$ale), 100)
23 | })
24 |
25 | test_that("Missing mandatory OpenFAIR factors are detected", {
26 | data("mc_quantitative_scenarios")
27 | bad_scen <- mc_quantitative_scenarios$scenario[[1]]
28 | bad_scen$parameters$tef <- NULL
29 | expect_error(run_simulation(bad_scen, 10L), regexp = "Missing")
30 | })
31 |
32 | test_that("Bad scenario parameters throw an error", {
33 | data("mc_quantitative_scenarios")
34 | bad_scen <- mc_quantitative_scenarios$scenario[[1]]
35 | bad_scen$parameters$tef$func <- "stats::rlnorm"
36 | expect_error(run_simulation(bad_scen, 10L), regexp = "Error")
37 | })
38 |
39 | test_that("Simulating multiple scenarios succeeds", {
40 | data("mc_quantitative_scenarios")
41 | scenarios <- mc_quantitative_scenarios[1:3, ]$scenario
42 | results <- run_simulations(scenarios[[1]], scenarios[[2]], scenarios[[3]], iterations = 10L)
43 | expect_is(results, "list")
44 | })
45 |
46 | test_that("Multiple simulations run fails when not given a scenario object", {
47 | data("mc_quantitative_scenarios")
48 | bad_scen <- mc_quantitative_scenarios$scenario[[1]]
49 | class(bad_scen) <- "list"
50 | expect_error(run_simulations(bad_scen, iterations = 10L), regexp = "object")
51 | })
52 |
53 | test_that("Multiple simulations deprecates the simulation_count parameters", {
54 | data("mc_quantitative_scenarios")
55 | good_scen <- mc_quantitative_scenarios$scenario[[1]]
56 | expect_error(run_simulations(good_scen, simulation_count = 10L), regexp = "iteration")
57 | })
58 |
--------------------------------------------------------------------------------
/data-raw/regenerate_data.R:
--------------------------------------------------------------------------------
1 | ## Regenerate sample data sets
2 | #library(evaluator)
3 | library(purrr)
4 | library(dplyr)
5 | library(future)
6 | plan(multiprocess)
7 |
8 | # read in and save domain mappings
9 | mc_domains <- readr::read_csv(here::here("inst/extdata/domains.csv"),
10 | col_types = readr::cols(
11 | domain_id = readr::col_character(),
12 | domain = readr::col_character()
13 | ))
14 | usethis::use_data(mc_domains, overwrite = TRUE)
15 |
16 | # read in capabilities
17 | mc_capabilities <- import_capabilities(domains = mc_domains)
18 | usethis::use_data(mc_capabilities, overwrite = TRUE)
19 |
20 | # read in mappings
21 | mc_mappings <- readr::read_csv(here::here("inst/extdata/qualitative_mappings.csv"),
22 | col_types = readr::cols(
23 | type = readr::col_character(),
24 | label = readr::col_character(),
25 | l = readr::col_double(),
26 | ml = readr::col_double(),
27 | h = readr::col_double(),
28 | conf = readr::col_double()))
29 | usethis::use_data(mc_mappings, overwrite = TRUE)
30 |
31 | # read in and save qualitative scenarios
32 | mc_qualitative_scenarios <- import_scenarios(domains = mc_domains)
33 | usethis::use_data(mc_qualitative_scenarios, overwrite = TRUE)
34 |
35 | # generate and save quantitative scenarios
36 | mc_quantitative_scenarios <- encode_scenarios(
37 | mc_qualitative_scenarios, mc_capabilities, mc_mappings)
38 | usethis::use_data(mc_quantitative_scenarios, overwrite = TRUE)
39 |
40 | # run simulations and save results
41 | mc_simulation_results <- mc_quantitative_scenarios %>%
42 | mutate(results = furrr::future_map(scenario, run_simulation, iterations = 1000, .progress = TRUE)) %>%
43 | select(-c(scenario, tcomm, scenario_description), scenario_id, domain_id, results)
44 |
45 | usethis::use_data(mc_simulation_results, overwrite = TRUE)
46 |
47 | # calculate and save domain summary
48 | mc_domain_summary <- summarize_domains(mc_simulation_results)
49 | usethis::use_data(mc_domain_summary, overwrite = TRUE)
50 |
51 | # calculate and save scenario summary
52 | #scenario_summary <- mutate(simulation_results, summary = map(results, summarize_scenario))
53 | mc_scenario_summary <- summarize_scenarios(mc_simulation_results)
54 | usethis::use_data(mc_scenario_summary, overwrite = TRUE)
55 |
--------------------------------------------------------------------------------
/.github/workflows/pkgdown.yaml:
--------------------------------------------------------------------------------
1 | on:
2 | push:
3 | branches:
4 | - main
5 | - master
6 | workflow_dispatch:
7 |
8 | name: pkgdown
9 |
10 | permissions:
11 | id-token: write
12 | contents: write
13 |
14 | jobs:
15 | pkgdown:
16 | runs-on: macOS-latest
17 | env:
18 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
19 | steps:
20 | - uses: actions/checkout@v2
21 |
22 | - uses: r-lib/actions/setup-r@v1
23 |
24 | - uses: r-lib/actions/setup-pandoc@v1
25 |
26 | - name: Query dependencies
27 | run: |
28 | install.packages('remotes')
29 | saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
30 | writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
31 | shell: Rscript {0}
32 |
33 | - name: Cache R packages
34 | uses: actions/cache@v2
35 | with:
36 | path: ${{ env.R_LIBS_USER }}
37 | key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
38 | restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-
39 |
40 | - name: Install dependencies
41 | run: |
42 | remotes::install_deps(dependencies = TRUE)
43 | install.packages("pkgdown", type = "binary")
44 | shell: Rscript {0}
45 |
46 | - name: Install package
47 | run: R CMD INSTALL .
48 |
49 | - name: Build site and reports
50 | run: |
51 | Rscript -e 'pkgdown::build_site(preview = FALSE)'
52 | Rscript ./scripts/generate_sample_reports.R
53 |
54 | - name: Configure AWS credentials
55 | uses: aws-actions/configure-aws-credentials@v1
56 | with:
57 | role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
58 | role-session-name: ${{ secrets.AWS_SESSION_NAME }}
59 | #aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
60 | #aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
61 | aws-region: us-west-2
62 |
63 | - name: Copy files to the website with the AWS CLI
64 | run: |
65 | aws s3 cp --recursive docs s3://${{ secrets.DEPLOY_BUCKET }}/
66 | #aws s3 sync . s3://my-s3-test-website-bucket
67 |
68 | - name: Copy reports to the website with the AWS CLI
69 | run: |
70 | aws s3 cp --recursive reports s3://${{ secrets.DEPLOY_BUCKET }}/reports/
71 | #aws s3 sync . s3://my-s3-test-website-bucket
72 |
73 |
--------------------------------------------------------------------------------
/_pkgdown.yml:
--------------------------------------------------------------------------------
1 | url: https://evaluator.tidyrisk.org
2 |
3 | template:
4 | params:
5 | bootswatch: flatly
6 | ganalytics: UA-87348339-3
7 |
8 | navbar:
9 | left:
10 | - icon: fa-home fa-lg
11 | href: index.html
12 | - text: "Geting Started"
13 | href: articles/usage.html
14 | - text: "Reference"
15 | href: reference/index.html
16 | - text: "Articles"
17 | menu:
18 | - text: "Advanced Customization"
19 | href: articles/customization.html
20 | - text: "Process"
21 | href: articles/process.html
22 | - text: Presentations
23 | menu:
24 | - text: "Tidyrisk Training Workshop"
25 | href: https://www.slideshare.net/davidski1/tidyrisk-workshop
26 | - text: "SiRAcon 2018"
27 | href: https://www.slideshare.net/davidski1/evaluator-siracon-2018-presentation
28 | - text: "News"
29 | href: news/index.html
30 | right:
31 | - icon: fa-github fa-lg fab
32 | text: "github"
33 | href: https://github.com/davidski/evaluator/
34 |
35 | reference:
36 | - title: Evaluator
37 | contents:
38 | - evaluator
39 | - title: Data Import and Cleansing Functions
40 | contents:
41 | - create_templates
42 | - derive_controls
43 | - encode_scenarios
44 | - starts_with("import")
45 | - read_qualitative_inputs
46 | - read_quantitative_inputs
47 | - openfair_example
48 | - split_sheet
49 | - validate_scenarios
50 | - title: Modelling Functions
51 | desc: OpenFAIR modelling components
52 | contents:
53 | - compare_tef_vuln
54 | - get_mean_control_strength
55 | - starts_with("openfair_")
56 | - run_simulation
57 | - run_simulations
58 | - starts_with("sample")
59 | - select_loss_opportunities
60 | - title: Reporting Functions
61 | contents:
62 | - starts_with("calculate")
63 | - derive_control_key
64 | - dollar_millions
65 | - explore_scenarios
66 | - exposure_histogram
67 | - starts_with("generate")
68 | - get_base_fontfamily
69 | - identify_outliers
70 | - loss_exceedance_curve
71 | - loss_scatterplot
72 | - risk_dashboard
73 | - theme_evaluator
74 | - title: Sample Data Set
75 | desc: Demonstration data set for the hypothetical MetroCare Hospital
76 | contents:
77 | - starts_with("mc_")
78 | - title: Summarization Functions
79 | contents:
80 | - starts_with("summarize")
81 | - title: Tidyrisk Scenario Class Functions
82 | contents:
83 | - contains("tidyrisk_scenario")
84 | - title: Tidyrisk Factor Class Functions
85 | contents:
86 | - contains("tidyrisk_factor")
87 |
--------------------------------------------------------------------------------
/vignettes/process.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "An Evaluator-driven Risk Management Process"
3 | output: rmarkdown::html_vignette
4 | vignette: >
5 | %\VignetteIndexEntry{An Evaluator-driven Risk Management Process}
6 | %\VignetteEngine{knitr::rmarkdown}
7 | %\VignetteEncoding{UTF-8}
8 | ---
9 |
10 | ```{r setup, include = FALSE}
11 | knitr::opts_chunk$set(
12 | collapse = TRUE,
13 | comment = "#>"
14 | )
15 | ```
16 |
17 | Working notes for building a risk assessment process using the Evaluator
18 | toolkit. This vignette details the basic flow used at some organizations to
19 | maintain reproducability of inputs and results. While other technologies and
20 | solutions may be employed, this workflow is intended to be easy to implement
21 | for users familiar with source control principles.
22 |
23 | ## Starting from Scratch
24 |
25 | - Create a starting Evaluator working directory via `create_templates()`
26 | - Customize the domains used in your organization by editing the `domains.csv`
27 | file to include the domains (and their abbreviations) which are appropriate for your
28 | risk domain and organization.
29 | - Edit the `survey.xlsx` spreadsheet to include the risk scenarios and the threats
30 | for each scenario which you wish to monitor on an ongoing basis.
31 | - Create a source code repository (such as a GitHub for Enterprise repo) for
32 | your ongoing risk analysis work.
33 | - Add a tag to designate the just committed files (without analysis) as your reference
34 | documentation.
35 |
36 | ## Performing an Analysis
37 | - Meet with subject matter experts for each domain and walk through each
38 | scenario and threat source.
39 | - Record answers in the survey.xlsx spreadsheet.
40 | - Run the analysis as detailed in the [usage](usage.html) vignette.
41 | - Commit the inputs and results to your source code repository.
42 | - Add a tag to reflect the state as of a certain point in time (i.e. FY18 Q1)
43 |
44 | ## What to Store
45 |
46 | If using the sample workflow, you will have two subdirectories located under
47 | a parent `evaluator` directory in your home folder. `inputs` contains the
48 | qualitative scenarios and input data files, while `results` stores the
49 | simulation results, including the summarized data files. Both directories
50 | together represent an analysis snapshot.
51 |
52 | ## Quick Start Script
53 |
54 | When using `create_templates()` a `run_analysis.R` file will be placed at the base
55 | of the `evaluator` directory. A sample ultra-quick workflow looks like:
56 |
57 | 1. Run `evaluator::create_templates("~/evaluator")`
58 | 2. Edit the inputs for your situation.
59 | 3. Run the quick start script with code like the following:
60 |
61 | ```{r eval=FALSE}
62 | base_dir <- "~/evaluator"
63 | source("~/evaluator/run_analysis.R")
64 | ```
65 |
--------------------------------------------------------------------------------
/tests/testthat/test-reports.R:
--------------------------------------------------------------------------------
1 | context("Reports")
2 | tmpdir <- tempdir(check = TRUE)
3 | tmpdata <- file.path(tmpdir, "data")
4 | dir.create(tmpdata)
5 | tmpinputs <- file.path(tmpdir, "inputs")
6 | dir.create(tmpinputs, showWarnings = FALSE)
7 |
8 | data("mc_simulation_results", package = "evaluator", envir = environment())
9 | saveRDS(mc_simulation_results, file = file.path(tmpdata, "simulation_results.rds"))
10 | data("mc_scenario_summary", package = "evaluator", envir = environment())
11 | saveRDS(mc_scenario_summary, file = file.path(tmpdata, "scenario_summary.rds"))
12 | data("mc_domain_summary", package = "evaluator", envir = environment())
13 | saveRDS(mc_domain_summary, file = file.path(tmpdata, "domain_summary.rds"))
14 |
15 | res <- c("domains.csv", "qualitative_mappings.csv", "risk_tolerances.csv") %>%
16 | purrr::map(~ file.copy(system.file("extdata", .x, package = "evaluator"),
17 | tmpinputs))
18 | data("mc_qualitative_scenarios", envir = environment())
19 | write.csv(mc_qualitative_scenarios, file = file.path(tmpinputs, "qualitative_scenarios.csv"),
20 | row.names = FALSE)
21 | data("mc_quantitative_scenarios", envir = environment())
22 | saveRDS(mc_quantitative_scenarios, file.path(tmpinputs, "quantitative_scenarios.rds"))
23 |
24 |
25 | test_that("Analyze report renders", {
26 |
27 | skip_if_not(rmarkdown::pandoc_available(),
28 | message = "Cannot test report generation without pandoc available.")
29 | purrr::walk(c("psych", "pander", "rmarkdown"),
30 | ~ skip_if_not_installed(.))
31 |
32 | file <- tempfile(fileext = ".html")
33 |
34 | result <- evaluate_promise(generate_report(input_directory = tmpinputs,
35 | results_directory = tmpdata,
36 | output_file = file,
37 | quiet = FALSE))
38 | expect_equivalent(normalizePath(result$result), normalizePath(file))
39 | unlink(file)
40 | })
41 |
42 | test_that("Risk Dashboard renders", {
43 |
44 | skip_if_not(rmarkdown::pandoc_available(),
45 | message = "Cannot test dashboard generation without pandoc available.")
46 | purrr::walk(c("rmarkdown", "shiny", "flexdashboard", "forcats"),
47 | ~ skip_if_not_installed(.))
48 |
49 | file <- tempfile(fileext = ".html")
50 |
51 | result <- evaluate_promise(risk_dashboard(input_directory = tmpinputs,
52 | results_directory = tmpdata,
53 | output_file = file,
54 | quiet = FALSE))
55 | expect_equivalent(normalizePath(result$result), normalizePath(file))
56 | # there should be no warnings
57 | expect_condition(result$warnings, regexp = NA)
58 | unlink(file)
59 | })
60 |
61 | unlink(c(tmpdata, tmpinputs), recursive = TRUE)
62 |
--------------------------------------------------------------------------------
/tests/testthat/test-summarize.R:
--------------------------------------------------------------------------------
1 | context("Summarization")
2 | test_that("Simulation summary", {
3 | data("mc_simulation_results")
4 | data("mc_scenario_summary")
5 | summarized_scenarios <- summarize_scenarios(mc_simulation_results)
6 | expect_equivalent(as.data.frame(summarized_scenarios),
7 | as.data.frame(mc_scenario_summary),
8 | tolerance = .01)
9 | })
10 | test_that("Scenario summary rejects non dataframe inputs", {
11 | data("mc_simulation_results")
12 | expect_error(summarize_scenario(mc_simulation_results$results), regexp = "dataframe")
13 | })
14 |
15 | test_that("Simulation summary handles NAs for tc/diff exceedance", {
16 | data("mc_simulation_results")
17 | mc_simulation_results[[1, "results"]][[1]]$mean_tc_exceedance <- NA
18 | #simulation_results[1, "mean_tc_exceedance"] <- NA
19 | mc_simulation_results[[10, "results"]][[1]]$mean_diff_exceedance <- NA
20 | dat <- mutate(mc_simulation_results,
21 | result_summary = map(results, summarize_scenario)) %>%
22 | select(-results)
23 | summarized_tc_exceedance <- dplyr::filter(dat, scenario_id == "RS-18") %>%
24 | tidyr::unnest(result_summary) %>%
25 | dplyr::pull(mean_tc_exceedance)
26 | expect_gt(summarized_tc_exceedance, 0)
27 | })
28 |
29 | test_that("Iteration-level summary", {
30 | data("mc_simulation_results")
31 | summarized_iterations <- summarize_iterations(mc_simulation_results$results)
32 | expect_lte(max(summarized_iterations$mean_tc_exceedance), 1)
33 | expect_gte(min(summarized_iterations$mean_tc_exceedance), 0)
34 | expect_lte(max(summarized_iterations$mean_diff_exceedance), 1)
35 | expect_gte(min(summarized_iterations$mean_diff_exceedance), 0)
36 | })
37 |
38 | test_that("Domain summary", {
39 | data("mc_simulation_results")
40 | data("mc_domain_summary")
41 |
42 | summarized_domains <- summarize_domains(mc_simulation_results)
43 |
44 | expect_equivalent(as.data.frame(summarized_domains),
45 | as.data.frame(mc_domain_summary), tolerance = 0.01)
46 | })
47 | test_that("Domain summary rejects non dataframe inputs", {
48 | data("mc_simulation_results")
49 | expect_error(summarize_domains(mc_simulation_results$results), regexp = "dataframe")
50 | })
51 |
52 | test_that("Summarize to disk", {
53 | tmpdata <- file.path(tempdir(), "data")
54 | dir.create(tmpdata)
55 |
56 | result <- summarize_to_disk(evaluator::mc_simulation_results,
57 | results_dir = tmpdata)
58 | expect_equal(nrow(result), 2)
59 | unlink(tmpdata, recursive = TRUE)
60 | })
61 |
62 | test_that("Summarize to disk - non-existant directory", {
63 | tmpdata <- file.path(tempdir(), "data")
64 |
65 | result <- summarize_to_disk(evaluator::mc_simulation_results,
66 | results_dir = tmpdata)
67 | expect_equal(nrow(result), 2)
68 | unlink(tmpdata, recursive = TRUE)
69 | })
70 |
71 |
--------------------------------------------------------------------------------
/inst/run_analysis.R:
--------------------------------------------------------------------------------
1 | # Quick Start script for Evaluator workflow
2 | # Process documented at https://evaluator.tidyrisk.org/articles/usage.html
3 |
4 | # This script is intended as a starting point for taking a directory of
5 | # inputs as created by evaluator::create_templates(), running simulations,
6 | # saving the result details & summary files, and preparing the default static
7 | # reports. The analyst MUST edit the template input files before running
8 | # this script to produce valid results.
9 |
10 | # Committing this script, along with the other contents of the base evaluator
11 | # directory, to source control provides a reproducable set of inputs & outputs
12 | # for a single analysis.
13 |
14 | # Setup -------------------------------------------------------------------
15 | library(evaluator)
16 | library(readr)
17 | library(purrr)
18 | library(dplyr)
19 | library(furrr)
20 | plan(multiprocess)
21 |
22 | if (!exists("base_dir") || !dir.exists(base_dir)) {
23 | stop("Set base_dir to your evaluator working directory before running this script.")
24 | }
25 |
26 | inputs_dir <- file.path(base_dir, "inputs")
27 | results_dir <- file.path(base_dir, "results")
28 |
29 | message("Beginning analysis run with input directory (", inputs_dir, ")",
30 | " and results directory (", results_dir, ")...")
31 |
32 | # Load and Validate -------------------------------------------------------
33 | message("Loading and validating inputs...")
34 |
35 | domains <- read_csv(file.path(inputs_dir, "domains.csv"),
36 | col_types = cols(.default = col_character()))
37 | import_spreadsheet(file.path(inputs_dir, "survey.xlsx"), domains, inputs_dir)
38 |
39 | qual_inputs <- read_qualitative_inputs(inputs_dir)
40 | qualitative_scenarios <- qual_inputs$qualitative_scenarios
41 | mappings <- qual_inputs$mappings
42 | capabilities <- qual_inputs$capabilities
43 | validate_scenarios(qualitative_scenarios, capabilities, domains, mappings)
44 |
45 | # Encode ------------------------------------------------------------------
46 | message("Encoding qualitative scenarios...")
47 | quantitative_scenarios <- encode_scenarios(qualitative_scenarios, capabilities,
48 | mappings)
49 | saveRDS(quantitative_scenarios,
50 | file = file.path(inputs_dir, "quantitative_scenarios.rds"))
51 |
52 | # Simulate ----------------------------------------------------------------
53 | message("Running simulations...")
54 | simulation_results <- quantitative_scenarios %>%
55 | mutate(results = furrr::future_map(scenario, run_simulation,
56 | iterations = 10000L, .progress = TRUE)) %>%
57 | select(-c(scenario, tcomm, scenario_description), scenario_id, domain_id, results)
58 | saveRDS(simulation_results, file = file.path(results_dir, "simulation_results.rds"))
59 |
60 | # Summarize ---------------------------------------------------------------
61 | message("Summarizing results...")
62 | summarize_to_disk(simulation_results = simulation_results, results_dir)
63 |
64 | # Report ---------------------------------------------------------------
65 | message("Generating reports...")
66 |
67 | ## Risk Dashboard
68 | risk_dashboard(inputs_dir, results_dir,
69 | file.path(results_dir, "risk_dashboard.html"))
70 |
71 | ## Long Form Report
72 | generate_report(inputs_dir, results_dir,
73 | file.path(results_dir, "risk_report.docx"),
74 | format = "word")
75 |
76 | message("Analysis complete.")
77 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at davidski@deadheaven.com. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/R/tidyrisk_scenario.R:
--------------------------------------------------------------------------------
1 | # Constructors ------------------------------------------------------------
2 |
3 | #' Construct a quantitative scenario object
4 | #'
5 | #' Supply one or more named lists in the format of `foo_params`,
6 | #' where each `foo` is an OpenFAIR factor name (e.g. tef, tc, diff, lm).
7 | #' Each factor should include a function name (`func`) to which the
8 | #' other named elements in the list are passed as parameters when
9 | #' sampling.
10 | #'
11 | #'
12 | #' @param ... One or more named OpenFAIR factor with parameters for sampling
13 | #' @param model Name of model to run
14 | #' @importFrom rlang list2
15 | #' @importFrom purrr every
16 | #' @export
17 | new_tidyrisk_scenario <- function(..., model = "openfair_tef_tc_diff_lm") {
18 | dots <- rlang::list2(...)
19 | if (any(names(dots) == "")) {
20 | stop("One or more parameters is unnamed.", call. = FALSE)
21 | }
22 | stopifnot(purrr::every(dots, is.list), is.character(model))
23 | names(dots) <- gsub( "_params", "", names(dots))
24 | scenario <- list(
25 | parameters = dots,
26 | model = model)
27 | class(scenario) <- c("tidyrisk_scenario", "list")
28 | scenario
29 | }
30 |
31 | #' @export
32 | #' @importFrom purrr modify
33 | #' @importFrom rlang list2
34 | #' @importFrom vctrs vec_cast
35 | #' @rdname new_tidyrisk_scenario
36 | tidyrisk_scenario <- function(..., model = "openfair_tef_tc_diff_lm") {
37 | dots <- rlang::list2(...)
38 | purrr::modify(dots, vctrs::vec_cast, list())
39 | model <- vctrs::vec_cast(model, character())
40 | validate_tidyrisk_scenario(new_tidyrisk_scenario(!!!dots, model = model))
41 | }
42 |
43 | #' Validates that a scenario object is well formed
44 | #'
45 | #' @param x An object
46 | #' @export
47 | #' @importFrom purrr every pluck
48 | validate_tidyrisk_scenario <- function(x) {
49 | # iterating a ragged list is currently not working as expected
50 | #if (!purrr::every(x$parameters, purrr::pluck, "func", .default = FALSE)) {
51 | # stop(
52 | # "All parameters must have a `func` element, specifing the sampling function",
53 | # call. = FALSE
54 | # )
55 | #}
56 |
57 | x
58 |
59 | }
60 |
61 | #' Test if the object is a tidyrisk_scenario
62 | #'
63 | #' This function returns `TRUE` for tidyrisk_scenario (or subclasses)
64 | #' and `FALSE` for all other objects.
65 | #'
66 | #' @param x An object
67 | #' @return `TRUE` if the object inherits from the `tidyrisk_scenario` class.
68 | #' @export
69 | is_tidyrisk_scenario <- function(x) {
70 | inherits(x, "tidyrisk_scenario")
71 | }
72 |
73 | #' Set an abbreviation when displaying an S3 column in a tibble
74 | #'
75 | #' @param x An object
76 | #' @export
77 | vec_ptype_abbr.tidyrisk_scenario <- function(x) {
78 | "r_scen"
79 | }
80 |
81 | # Formatters --------------------------------------------------------------
82 |
83 |
84 | #' Default printing of a tidyrisk_scenario
85 | #'
86 | #' Basic printing of a tidyrisk scenario
87 | #'
88 | #' @param x A tidyrisk_scenario
89 | #' @param ... Currently not used
90 | #' @export
91 | #' @importFrom cli cat_line
92 | print.tidyrisk_scenario <- function(x, ...) {
93 | cli::cat_line("# Scenario model: ", x$model)
94 | cli::cat_line("# Defined parameters: ", paste0(names(x$parameters), collapse = ", "))
95 | #NextMethod(x)
96 |
97 | invisible(x)
98 | }
99 |
100 | #' Coerce the parameters of a tidyrisk_scenario to a tibble
101 | #'
102 | #' @param x A tidyrisk_scenario
103 | #' @param ... Currently not used
104 | #' @export
105 | #' @importFrom cli cat_line
106 | #' @importFrom purrr map_depth
107 | #' @importFrom dplyr bind_rows
108 | as_tibble.tidyrisk_scenario <- function(x, ...) {
109 | cli::cat_line("# Scenario model: ", x$model)
110 | purrr::map_depth(x$parameters, .depth = 1, dplyr::bind_rows, .id = "id") %>%
111 | dplyr::bind_rows(.id = "openfair_factor")
112 | }
113 |
114 | #' @rdname as_tibble.tidyrisk_scenario
115 | #' @export
116 | as.data.frame.tidyrisk_scenario <- as_tibble.tidyrisk_scenario
117 |
--------------------------------------------------------------------------------
/tests/testthat/test-encode.R:
--------------------------------------------------------------------------------
1 | context("Encodings")
2 | test_that("Scenario Encoding", {
3 | qualitative_scenarios <- data.frame(scenario_id = 1:2,
4 | scenario = c("Scenario A.", "Scenario B."),
5 | tcomm = c("Organizational Leadership", "Organizational Leadership"),
6 | tef = c("frequent", "frequent"),
7 | tc = c("medium", "medium"),
8 | lm = c("medium", "medium"),
9 | domain_id = c("ORG", "ORG"),
10 | controls = c("1, 5, 7, 32, 14",
11 | "14, 15, 16"),
12 | stringsAsFactors = FALSE)
13 | capabilities <- data.frame(capability_id = c(1L, 5L, 7L, 32L, 14L, 15L, 16L),
14 | domain_id = c("ORG", "ORG", "ORG", "ORG", "ORG", "ORG", "ORG"),
15 | capability = c("Capability 1.", "Capability 5.", "Capability 7", "Capability 32.", "Capability 14.", "Capability 15.", "Capability 16."),
16 | diff = c("5 - Optimized", "4 - Managed", "1 - Initial", "4 - Managed", "4 - Managed", "2 - Repeatable", "2 - Repeatable"),
17 | stringsAsFactors = FALSE)
18 | mappings <- data.frame(type = c("tef", "tef", "tef", "tc", "tc", "tc", "diff", "diff", "diff", "diff", "diff", "lm", "lm", "lm"),
19 | label = c("frequent", "occasional", "rare", "high", "medium", "low", "5 - Optimized", "4 - Managed", "3 - Defined", "2 - Repeatable", "1 - Initial", "high", "medium", "low"),
20 | l = c(10L, 1L, 0L, 50L, 33L, 0L, 70L, 50L, 33L, 20L, 0L, 1000000L, 10000L, 100L),
21 | ml = c(24, 6, 0.1, 75, 50, 16, 85, 70, 50, 30, 10, 2e+06, 20000, 200),
22 | h = c(52L, 12L, 1L, 98L, 60L, 30L, 98L, 84L, 60L, 50L, 30L, 5000000L, 500000L, 10000L),
23 | conf = c(4L, 4L, 4L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L),
24 | stringsAsFactors = FALSE)
25 | dat <- encode_scenarios(qualitative_scenarios, capabilities, mappings)
26 | expect_equal(nrow(dat), 2)
27 | expect_true(is.data.frame(dat))
28 | })
29 | test_that("Control Encoding", {
30 | capability_ids <- "1, 7"
31 | capabilities <- data.frame(capability_id = c(1L, 5L, 7L, 32L, 14L, 15L, 16L),
32 | domain_id = c("ORG", "ORG", "ORG", "ORG", "ORG", "ORG", "ORG"),
33 | capability = c("Capability 1.", "Capability 5.", "Capability 7.", "Capability 32.", "Capability 14.", "Capability 15.", "Capability 16."),
34 | diff = c("5 - Optimized", "4 - Managed", "1 - Initial", "4 - Managed", "4 - Managed", "2 - Repeatable", "2 - Repeatable"),
35 | stringsAsFactors = FALSE)
36 | mappings <- data.frame(type = c("diff", "diff"), label = c("5 - Optimized", "1 - Initial"),
37 | l = c(70, 0), ml = c(80, 20), h = c(95, 30),
38 | conf = 3, stringsAsFactors = FALSE)
39 | dat <- derive_controls(capability_ids, capabilities, mappings)
40 | expect_type(dat, "list")
41 | expect_named(dat, c("1", "7"))
42 | expect_equal(length(dat), 2)
43 | })
44 |
45 | test_that("Control ID Mappings", {
46 | capability_ids <- "1, 7"
47 | capabilities <- data.frame(capability_id = c(1L, 5L, 7L, 32L, 14L, 15L, 16L),
48 | domain_id = c("ORG", "ORG", "ORG", "ORG", "ORG", "ORG", "ORG"),
49 | capability = c("Capability 1.", "Capability 5.", "Capability 7.", "Capability 32.", "Capability 14.", "Capability 15.", "Capability 16."),
50 | diff = c("5 - Optimized", "4 - Managed", "1 - Initial", "4 - Managed", "4 - Managed", "2 - Repeatable", "2 - Repeatable"),
51 | stringsAsFactors = FALSE)
52 | dat <- derive_control_key(capability_ids, capabilities)
53 | expect_type(dat, "list")
54 | expect_named(dat, c("1", "7"))
55 | expect_equal(length(dat), 2)
56 | })
57 |
--------------------------------------------------------------------------------
/inst/rmd/styles/html-styles.css:
--------------------------------------------------------------------------------
1 | p {
2 | font-family: Open Sans, sans-serif;
3 | }
4 |
5 | ul li {
6 | font-family: Open Sans, sans-serif;
7 | }
8 |
9 | ol li {
10 | font-family: Open Sans, sans-serif;
11 | }
12 |
13 |
14 | h1 {
15 | font-family: Open Sans Condensed, sans-serif;
16 | color: #430098;
17 | }
18 |
19 | h2, h3, h4, h5, h6 {
20 | font-family: Open Sans Condensed, sans-serif;
21 | color: #7C858C;
22 | }
23 |
24 | a {
25 | color: #6e5baa;
26 | }
27 |
28 | p {
29 | color: #333D47;
30 | }
31 |
32 | body {
33 | font-family: Open Sans, sans-serif;
34 | color: #333D47;
35 | }
36 |
37 | /* flexdashboard styles */
38 | .section.sidebar {
39 | top: 51px;
40 | background-color: #C3C5C8;
41 | }
42 |
43 | .bg-danger {
44 | background-color: #d60057;
45 | }
46 |
47 | .bg-warning {
48 | background-color: #c227b9;
49 | }
50 |
51 | .bg-primary {
52 | color: #fff;
53 | background-color: #00a1df;
54 | }
55 |
56 |
57 | .navbar-inverse {
58 | background-color: #211746;
59 | border-color: #1967be;
60 | }
61 | .navbar-inverse .navbar-brand {
62 | color: #ffffff;
63 | }
64 | .navbar-inverse .navbar-brand:hover,
65 | .navbar-inverse .navbar-brand:focus {
66 | color: #ffffff;
67 | background-color: none;
68 | }
69 | .navbar-inverse .navbar-text {
70 | color: #ffffff;
71 | }
72 | .navbar-inverse .navbar-nav > li > a {
73 | color: #ffffff;
74 | }
75 | .navbar-inverse .navbar-nav > li > a:hover,
76 | .navbar-inverse .navbar-nav > li > a:focus {
77 | color: #ffffff;
78 | background-color: #00a1df;
79 | }
80 | .navbar-inverse .navbar-nav > .active > a,
81 | .navbar-inverse .navbar-nav > .active > a:hover,
82 | .navbar-inverse .navbar-nav > .active > a:focus {
83 | color: #ffffff;
84 | background-color: #00a1df;
85 | }
86 | .navbar-inverse .navbar-nav > .disabled > a,
87 | .navbar-inverse .navbar-nav > .disabled > a:hover,
88 | .navbar-inverse .navbar-nav > .disabled > a:focus {
89 | color: #ffffff;
90 | background-color: transparent;
91 | }
92 | .navbar-inverse .navbar-toggle {
93 | border-color: transparent;
94 | }
95 | .navbar-inverse .navbar-toggle:hover,
96 | .navbar-inverse .navbar-toggle:focus {
97 | background-color: #00a1df;
98 | }
99 | .navbar-inverse .navbar-toggle .icon-bar {
100 | background-color: #ffffff;
101 | }
102 | .navbar-inverse .navbar-collapse,
103 | .navbar-inverse .navbar-form {
104 | border-color: #1a6ecc;
105 | }
106 | .navbar-inverse .navbar-nav > .open > a,
107 | .navbar-inverse .navbar-nav > .open > a:hover,
108 | .navbar-inverse .navbar-nav > .open > a:focus {
109 | background-color: #00a1df;
110 | color: #ffffff;
111 | }
112 | @media (max-width: 767px) {
113 | .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header {
114 | border-color: #00a1df;
115 | }
116 | .navbar-inverse .navbar-nav .open .dropdown-menu .divider {
117 | background-color: #00a1df;
118 | }
119 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a {
120 | color: #ffffff;
121 | }
122 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover,
123 | .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus {
124 | color: #ffffff;
125 | background-color: #00a1df;
126 | }
127 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a,
128 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover,
129 | .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus {
130 | color: #ffffff;
131 | background-color: #00a1df;
132 | }
133 | .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a,
134 | .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover,
135 | .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus {
136 | color: #ffffff;
137 | background-color: transparent;
138 | }
139 | }
140 | .navbar-inverse .navbar-link {
141 | color: #ffffff;
142 | }
143 | .navbar-inverse .navbar-link:hover {
144 | color: #ffffff;
145 | }
146 | .navbar-inverse .btn-link {
147 | color: #ffffff;
148 | }
149 | .navbar-inverse .btn-link:hover,
150 | .navbar-inverse .btn-link:focus {
151 | color: #ffffff;
152 | }
153 | .navbar-inverse .btn-link[disabled]:hover,
154 | fieldset[disabled] .navbar-inverse .btn-link:hover,
155 | .navbar-inverse .btn-link[disabled]:focus,
156 | fieldset[disabled] .navbar-inverse .btn-link:focus {
157 | color: #ffffff;
158 | }
159 |
--------------------------------------------------------------------------------
/R/simulate.R:
--------------------------------------------------------------------------------
1 | #' Run simulations for a scenario
2 | #'
3 | #' Given a quantitative scenario object of type `tidyrisk_scenario`, run an
4 | #' OpenFAIR Monte Carlo simulation.
5 | #'
6 | #' @importFrom dplyr progress_estimated bind_rows mutate row_number
7 | #' @importFrom purrr safely is_null map_lgl transpose simplify keep some
8 | #' @importFrom tidyr nest
9 | #' @importFrom rlang .data exec
10 | #'
11 | #' @param scenario A \link{tidyrisk_scenario} object.
12 | #' @param iterations Number of iterations to run on each scenario.
13 | #' @param simulation_count **DEPRECATED** Number of simulations to perform.
14 | #' @param ale_maximum Maximum practical annual losses.
15 | #' @param verbose Whether verbose console output is requested.
16 | #'
17 | #' @export
18 | #' @return Dataframe of results.
19 | #' @examples
20 | #' data(mc_quantitative_scenarios)
21 | #' run_simulation(mc_quantitative_scenarios$scenario[[1]], 10)
22 | run_simulation <- function(scenario, iterations = 10000L,
23 | ale_maximum = NULL,
24 | verbose = FALSE, simulation_count = NULL) {
25 |
26 | if (!is.null(simulation_count)) stop("simulation_count is deprecated. use `iterations` instead.", call. = FALSE)
27 |
28 | if (!is_tidyrisk_scenario(scenario)) {
29 | stop("Scenario must be a tidyrisk_scenario object", call. = FALSE)
30 | }
31 |
32 | # check for required elements
33 | required_elements <- formals(scenario$model) %>% names %>%
34 | setdiff(c("n", "verbose"))
35 | if (purrr::some(scenario$parameters[required_elements], is.null)) {
36 | stop("Missing one or more required elements.", call. = FALSE)
37 | }
38 |
39 | #model <- rlang::sym(model) # convert characters to symbol
40 | func <- scenario$model
41 | params <- c(scenario$parameters, list(n = iterations, verbose = verbose))
42 | wrapped_calc <- function(x, .pb = NULL) {
43 | if ((!is.null(.pb)) & inherits(.pb, "Progress") && (.pb$i < .pb$n)) .pb$tick()$print()
44 |
45 | safe_calculate <- purrr::safely(eval(rlang::parse_expr(func)))
46 | rlang::exec("safe_calculate", !!!params)
47 | }
48 |
49 | simulation_results <- wrapped_calc(scenario)
50 |
51 | if (!is.null(simulation_results$error)) {
52 | stop("Errors encountered with scenarios:\n",
53 | scenario,
54 | paste0("Error: ", simulation_results$error,
55 | collapse = "\n"))
56 | }
57 |
58 | simulation_results <- simulation_results$result
59 |
60 | # apply a maximum per year ALE, if requested
61 | if (!(is.null(ale_maximum))) simulation_results$ale <- pmin(simulation_results$ale, ale_maximum)
62 |
63 | # store the date on which this simulation was generated
64 | attr(simulation_results, "generated_on") <- Sys.time()
65 | # store the number of iterations performed
66 | attr(simulation_results, "iterations") <- iterations
67 |
68 | simulation_results
69 |
70 | }
71 |
72 |
73 |
74 | #' Run simulations for a list of scenarios
75 | #'
76 | #' Given a list of quantitative scenario objects of type `tidyrisk_scenario`,
77 | #' run a OpenFAIR Monte Carlo simulation for each scenario.
78 | #'
79 | #' @inheritParams run_simulation
80 | #' @param ... Additional `tidyrisk_scenario` objects to simulate.
81 | #'
82 | #' @importFrom purrr map every
83 | #' @importFrom dplyr mutate
84 | #' @importFrom rlang .data
85 | #'
86 | #' @return A list of one dataframe of results for each scenario.
87 | #' @export
88 | #' @examples
89 | #' # fetch three scenarios for this example
90 | #' data(mc_quantitative_scenarios)
91 | #' scenario_a <- mc_quantitative_scenarios$scenario[[1]]
92 | #' scenario_b <- mc_quantitative_scenarios$scenario[[2]]
93 | #' scenario_c <- mc_quantitative_scenarios$scenario[[3]]
94 | #' run_simulations(scenario_a, scenario_b, scenario_c, iterations = 10)
95 | #'
96 | run_simulations <- function(scenario, ..., iterations = 10000L,
97 | ale_maximum = NULL,
98 | verbose = FALSE, simulation_count = NULL) {
99 |
100 | scenarios <- list(scenario, ...)
101 | if (!purrr::every(scenarios, is_tidyrisk_scenario)) {
102 | stop("All scenarios must be tidyrisk_scenario objects", call. = FALSE)
103 | }
104 |
105 | if (!is.null(simulation_count)) stop("simulation_count is deprecated. use `iterations` instead.", call. = FALSE)
106 |
107 | map(scenarios, run_simulation, iterations, ale_maximum, verbose)
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/R/utils.R:
--------------------------------------------------------------------------------
1 | #' Helper function to verify package availability
2 | #'
3 | #' @importFrom purrr map_df
4 | #' @importFrom tibble tibble
5 | #' @param packages Packages to verify are available.
6 | #' @param func Calling function.
7 | #'
8 | #' @return Invisible
9 | #' @noRd
10 | #' @keywords internal
11 | #' \dontrun{
12 | #' check_availability(packages = c("ggplot2", "dplyr"), func = "my_function")
13 | #' }
14 | check_availability <- function(packages, func) {
15 | res <- purrr::map_df(packages,
16 | ~ tibble::tibble(package = .x,
17 | available = requireNamespace(.x, quietly=TRUE)))
18 | if (sum(res$available) != length(packages)) {
19 | stop(func, " requires the following packages which are not available: ",
20 | paste0(res[res$available == FALSE, ]$package, collapse = ", "), "\n",
21 | "Please install and try again.", call. = FALSE)
22 | }
23 |
24 | invisible()
25 |
26 | }
27 |
28 | #' Format dollar amounts in terms of millions of USD
29 | #'
30 | #' Given a number, return a string formatted in terms of millions of dollars.
31 | #'
32 | #' @importFrom dplyr %>%
33 | #' @param x A number.
34 | #' @return String in the format of $xM.
35 | #' @export
36 | #' @examples
37 | #' dollar_millions(1.523 * 10^6)
38 | dollar_millions <- function(x) {
39 | # paste0('$', x / 10 ^ 6, 'M')
40 | x <- (x/10^6) %>% round(digits = 2)
41 | paste0("$", x, "M")
42 | }
43 |
44 | #' Unnest a summarized results dataframe, adding outlier information
45 | #'
46 | #' Given a summarized results dataframe, unnest the summary results
47 | #' column and use the value at risk (VaR) column to identify all the
48 | #' elements that are outliers (having a VaR >= two standard deviations)
49 | #'
50 | #' @param results Scenario summary results
51 | #' @importFrom dplyr mutate
52 | #' @importFrom tidyr unnest
53 | #' @export
54 | #' @return The supplied dataframe with the following additional columns:
55 | #' - `ale_var_zscore` - Annual loss z-score
56 | #' - `outlier` - Logical flag when the z-score is greater than or equal to two
57 | #' @examples
58 | #' data(mc_scenario_summary)
59 | #' identify_outliers(mc_scenario_summary)
60 | identify_outliers <- function(results) {
61 | results %>% #tidyr::unnest(summary) %>%
62 | dplyr::mutate(ale_var_zscore = as.vector(scale(.data$ale_var)),
63 | outlier = .data$ale_var_zscore >= 2)
64 | }
65 |
66 |
67 | #' Calculate maximum losses
68 | #'
69 | #' Calculate the biggest single annual loss for each scenario, as well as
70 | #' the minimum and maximum ALE across all iterations. Calculations both
71 | #' with and without outliers (if passed) are returned.
72 | #'
73 | #' @importFrom dplyr filter group_by summarize ungroup union
74 | #' @importFrom tidyr unnest
75 | #' @importFrom rlang .data
76 | #' @param simulation_results Simulation results dataframe.
77 | #' @param scenario_outliers Optional vector of IDs of outlier scenarios.
78 | #' @return A dataframe with the following columns:
79 | #' - `iteration` - index of the iteration
80 | #' - `biggest_single_scenario_loss` - the biggest annual loss in that iteration,
81 | #' - `min_loss` - the smallest annual loss in that iteration,
82 | #' - `max_loss` - the total annual losses in that iteration
83 | #' - `outliers` - logical of whether or not outliers are included
84 | #' @export
85 | #' @examples
86 | #' data(mc_simulation_results)
87 | #' calculate_max_losses(mc_simulation_results)
88 | calculate_max_losses <- function(simulation_results, scenario_outliers = NULL) {
89 | max_loss <- tidyr::unnest(simulation_results, .data$results) %>%
90 | dplyr::filter(!.data$scenario_id %in% scenario_outliers) %>%
91 | dplyr::group_by(.data$iteration) %>%
92 | dplyr::summarize(biggest_single_scenario_loss = max(.data$ale),
93 | min_loss = min(.data$ale), max_loss = sum(.data$ale),
94 | outliers = FALSE) %>%
95 | dplyr::ungroup()
96 | # if we're not passed any outliers, don't bother calculating outliers
97 | if (is.null(scenario_outliers)) return(max_loss)
98 |
99 | max_loss_w_outliers <- tidyr::unnest(simulation_results, .data$results) %>%
100 | dplyr::group_by(.data$iteration) %>%
101 | dplyr::summarize(biggest_single_scenario_loss = max(.data$ale),
102 | min_loss = min(.data$ale),
103 | max_loss = sum(.data$ale), outliers = TRUE) %>%
104 | dplyr::ungroup()
105 | dplyr::union(max_loss, max_loss_w_outliers)
106 | }
107 |
--------------------------------------------------------------------------------
/R/validate.R:
--------------------------------------------------------------------------------
1 | #' Validate qualitative scenario data
2 | #'
3 | #' Run a set of basic consistency checks on the key qualitative data inputs
4 | #' (scenarios, capabilities, domains, and mappings).
5 | #'
6 | #' Checks that:
7 | #' - All scenarios are distinct
8 | #' - All controls referenced in scenarios are defined in the controls table
9 | #' - All controls are distinct
10 | #'
11 | #' @importFrom dplyr tally filter left_join rename anti_join rowwise ungroup
12 | #' @importFrom rlang .data
13 | #' @importFrom stringi stri_split_fixed
14 | #' @importFrom tidyr separate_rows
15 | #' @param scenarios Dataframe of qualitative scenarios.
16 | #' @param capabilities Dataframe of capabilities.
17 | #' @param domains Dataframe of domain mappings.
18 | #' @param mappings Dataframe of qualitative to quantitative mappings.
19 | #'
20 | #' @export
21 | #' @return An invisible boolean as to success/failure of validation steps.
22 | #' @examples
23 | #' \dontrun{
24 | #' validate_scenarios(scenarios, capabilities, domains, mappings)
25 | #' }
26 | validate_scenarios <- function(scenarios, capabilities, domains, mappings) {
27 |
28 | validated <- TRUE
29 |
30 | # Verify there are no duplicate scenarios
31 | scenarios %>% dplyr::group_by_at(.vars = "scenario_id") %>% dplyr::tally() %>%
32 | dplyr::filter(.data$n > 1) %>%
33 | dplyr::left_join(scenarios, by = "scenario_id") %>%
34 | dplyr::rename(times_duplicated = .data$n) ->
35 | duplicate_scenarios
36 | if (nrow(duplicate_scenarios) != 0) {
37 | warning(paste("Duplicate scenarios found:",
38 | duplicate_scenarios$scenario_id, collapse = "\n"),
39 | call. = FALSE)
40 | validated <- FALSE
41 | }
42 |
43 | # Are all the capabilities referenced in the scenarios defined?
44 | missing_capabilities <- scenarios %>%
45 | tidyr::separate_rows(.data$controls, sep = ", ", convert = FALSE) %>%
46 | dplyr::anti_join(capabilities, by = c("controls" = "capability_id"))
47 | if (nrow(missing_capabilities) != 0) {
48 | warning(paste("Scenarios with undefined capabilities:",
49 | missing_capabilities$scenario_id, collapse = "\n"),
50 | call. = FALSE)
51 | validated <- FALSE
52 | }
53 |
54 | # Verify there are no duplicate controls
55 | capabilities %>% dplyr::group_by_at(.vars = "capability_id") %>% dplyr::tally() %>%
56 | dplyr::filter(.data$n > 1) %>%
57 | dplyr::left_join(capabilities, by = "capability_id") %>%
58 | dplyr::rename(times_duplicated = .data$n) -> duplicate_capabilities
59 | if (nrow(duplicate_capabilities) != 0) {
60 | warning(paste("Duplicate capabilities found:",
61 | duplicate_capabilities$capability_id, collapse = "\n"),
62 | call. = FALSE)
63 | validated <- FALSE
64 | }
65 |
66 | ## ----check_qualitative_mappings------------------------------------------
67 | stop_message <- NULL
68 |
69 | # TEF
70 | if (!all((tolower(distinct(scenarios, .data$tef)$tef) %in% filter(mappings, .data$type=="tef")$label))) {
71 | stop_message <- paste(stop_message,
72 | "There are qualitative TEF values in the scenarios which are not found in the mappings", sep = "\n")
73 | }
74 | # TC
75 | if (!all((tolower(distinct(scenarios, .data$tc)$tc) %in% filter(mappings, .data$type=="tc")$label))) {
76 | stop_message <- paste(stop_message,
77 | "There are qualitative TC values in the scenarios which are not found in the mappings", sep = "\n")
78 | }
79 | # DIFF
80 | if (!all((tolower(distinct(capabilities, .data$diff)$diff) %in% tolower(filter(mappings, .data$type=="diff")$label)))) {
81 | stop_message <- paste(stop_message,
82 | "There are qualitative DIFF values in the capabilities which are not found in the mappings", sep = "\n")
83 | }
84 | # LM
85 | if (!all((tolower(distinct(scenarios, .data$lm)$lm) %in% filter(mappings, .data$type=="lm")$label))) {
86 | stop_message <- paste(stop_message,
87 | "There are qualitative LM values in the scenarios which not found in the mappings", sep = "\n")
88 | }
89 |
90 | if (!is.null(stop_message)) {
91 | warning(stop_message, call. = FALSE)
92 | validated <- FALSE
93 | }
94 |
95 | # add the number of controls applicable to each scenario as a validation step
96 | scenarios <- scenarios %>%
97 | dplyr::rowwise() %>%
98 | dplyr::mutate(control_count =
99 | length(stringi::stri_split_fixed(.data$controls,
100 | ", ",
101 | simplify = TRUE))) %>%
102 | dplyr::ungroup()
103 |
104 | invisible(validated)
105 | }
106 |
--------------------------------------------------------------------------------
/NAMESPACE:
--------------------------------------------------------------------------------
1 | # Generated by roxygen2: do not edit by hand
2 |
3 | S3method(as.data.frame,tidyrisk_scenario)
4 | S3method(as_tibble,tidyrisk_scenario)
5 | S3method(print,tidyrisk_scenario)
6 | export("%>%")
7 | export(calculate_max_losses)
8 | export(compare_tef_vuln)
9 | export(create_templates)
10 | export(create_tidyrisk_scenario_skeleton)
11 | export(derive_control_key)
12 | export(derive_controls)
13 | export(dollar_millions)
14 | export(encode_scenarios)
15 | export(explore_scenarios)
16 | export(exposure_histogram)
17 | export(generate_event_outcomes_plot)
18 | export(generate_heatmap)
19 | export(generate_report)
20 | export(generate_scatterplot)
21 | export(get_base_fontfamily)
22 | export(get_mean_control_strength)
23 | export(identify_outliers)
24 | export(import_capabilities)
25 | export(import_scenarios)
26 | export(import_spreadsheet)
27 | export(is_tidyrisk_scenario)
28 | export(load_data)
29 | export(loss_exceedance_curve)
30 | export(loss_scatterplot)
31 | export(new_tidyrisk_scenario)
32 | export(openfair_example)
33 | export(openfair_tef_tc_diff_lm)
34 | export(openfair_tef_tc_diff_plm_sr)
35 | export(read_qualitative_inputs)
36 | export(read_quantitative_inputs)
37 | export(risk_dashboard)
38 | export(run_simulation)
39 | export(run_simulations)
40 | export(sample_diff)
41 | export(sample_lef)
42 | export(sample_lm)
43 | export(sample_tc)
44 | export(sample_tef)
45 | export(sample_vuln)
46 | export(select_loss_opportunities)
47 | export(summarize_domains)
48 | export(summarize_iterations)
49 | export(summarize_scenario)
50 | export(summarize_scenarios)
51 | export(summarize_to_disk)
52 | export(theme_evaluator)
53 | export(tidyrisk_scenario)
54 | export(validate_scenarios)
55 | export(validate_tidyrisk_scenario)
56 | export(vec_ptype_abbr.tidyrisk_scenario)
57 | import(dplyr)
58 | import(ggplot2)
59 | importFrom(cli,cat_bullet)
60 | importFrom(cli,cat_line)
61 | importFrom(cli,cat_print)
62 | importFrom(cli,cat_rule)
63 | importFrom(cli,style_bold)
64 | importFrom(dplyr,"%>%")
65 | importFrom(dplyr,anti_join)
66 | importFrom(dplyr,arrange)
67 | importFrom(dplyr,bind_rows)
68 | importFrom(dplyr,case_when)
69 | importFrom(dplyr,desc)
70 | importFrom(dplyr,do)
71 | importFrom(dplyr,filter)
72 | importFrom(dplyr,group_by)
73 | importFrom(dplyr,group_by_at)
74 | importFrom(dplyr,left_join)
75 | importFrom(dplyr,mutate)
76 | importFrom(dplyr,mutate_at)
77 | importFrom(dplyr,percent_rank)
78 | importFrom(dplyr,progress_estimated)
79 | importFrom(dplyr,pull)
80 | importFrom(dplyr,rename)
81 | importFrom(dplyr,row_number)
82 | importFrom(dplyr,rowwise)
83 | importFrom(dplyr,select)
84 | importFrom(dplyr,summarize)
85 | importFrom(dplyr,tally)
86 | importFrom(dplyr,ungroup)
87 | importFrom(dplyr,union)
88 | importFrom(ggplot2,"%+replace%")
89 | importFrom(ggplot2,element_blank)
90 | importFrom(ggplot2,element_text)
91 | importFrom(ggplot2,theme)
92 | importFrom(ggplot2,theme_minimal)
93 | importFrom(magrittr,"%>%")
94 | importFrom(mc2d,rpert)
95 | importFrom(purrr,every)
96 | importFrom(purrr,flatten)
97 | importFrom(purrr,is_list)
98 | importFrom(purrr,is_null)
99 | importFrom(purrr,keep)
100 | importFrom(purrr,map)
101 | importFrom(purrr,map_dbl)
102 | importFrom(purrr,map_depth)
103 | importFrom(purrr,map_df)
104 | importFrom(purrr,map_dfr)
105 | importFrom(purrr,map_int)
106 | importFrom(purrr,map_lgl)
107 | importFrom(purrr,modify)
108 | importFrom(purrr,pluck)
109 | importFrom(purrr,pmap)
110 | importFrom(purrr,safely)
111 | importFrom(purrr,simplify)
112 | importFrom(purrr,simplify_all)
113 | importFrom(purrr,some)
114 | importFrom(purrr,transpose)
115 | importFrom(purrr,walk2)
116 | importFrom(readr,col_character)
117 | importFrom(readr,col_double)
118 | importFrom(readr,col_integer)
119 | importFrom(readr,cols)
120 | importFrom(readr,read_csv)
121 | importFrom(readr,write_csv)
122 | importFrom(readxl,read_excel)
123 | importFrom(rlang,.data)
124 | importFrom(rlang,as_function)
125 | importFrom(rlang,ensym)
126 | importFrom(rlang,exec)
127 | importFrom(rlang,list2)
128 | importFrom(rlang,set_names)
129 | importFrom(rstudioapi,insertText)
130 | importFrom(scales,comma)
131 | importFrom(scales,dollar)
132 | importFrom(scales,percent)
133 | importFrom(stats,median)
134 | importFrom(stats,quantile)
135 | importFrom(stats,sd)
136 | importFrom(stringi,stri_split_fixed)
137 | importFrom(tibble,add_row)
138 | importFrom(tibble,as_tibble)
139 | importFrom(tibble,deframe)
140 | importFrom(tibble,rownames_to_column)
141 | importFrom(tibble,tibble)
142 | importFrom(tidyr,gather)
143 | importFrom(tidyr,nest)
144 | importFrom(tidyr,separate_rows)
145 | importFrom(tidyr,unnest)
146 | importFrom(utils,data)
147 | importFrom(vctrs,new_vctr)
148 | importFrom(vctrs,stop_incompatible_cast)
149 | importFrom(vctrs,vec_assert)
150 | importFrom(vctrs,vec_cast)
151 | importFrom(vctrs,vec_data)
152 | importFrom(vctrs,vec_proxy_equal)
153 | importFrom(viridis,scale_fill_viridis)
154 | importFrom(viridis,viridis)
155 |
--------------------------------------------------------------------------------
/R/tidyrisk_factor.R:
--------------------------------------------------------------------------------
1 | # Constructors ------------------------------------------------------------
2 |
3 | #' Construct a tidyrisk_factor object
4 | #'
5 | #' @param samples samples
6 | #' @param factor_label fl
7 | #' @param details details
8 | #' @name tidyrisk_factor
9 | NULL
10 |
11 | #' @rdname tidyrisk_factor
12 | #' @importFrom vctrs vec_assert new_vctr
13 | new_tidyrisk_factor <- function(samples = double(), factor_label = character(),
14 | details = list()) {
15 | vctrs::vec_assert(samples, double())
16 | vctrs::vec_assert(factor_label, character())
17 | if (!factor_label %in% c("TF", "TEF", "TC", "DIFF", "LM", "PLM", "SLF", "SLM")) {
18 | stop("Factor label is not a supported OpenFAIR type.", call. = FALSE)
19 | }
20 | vctrs::vec_assert(details, list())
21 | vctrs::new_vctr(samples, factor_label = factor_label, details = details,
22 | class = "tidyrisk_factor")
23 | }
24 |
25 | #' @rdname tidyrisk_factor
26 | tidyrisk_factor <- function(samples, factor_label, details = list()) {
27 | samples <- vctrs::vec_cast(samples, double())
28 | factor_label <- vctrs::vec_cast(factor_label, character())
29 | details <- vctrs::vec_cast(details, list())
30 | new_tidyrisk_factor(samples, factor_label, details)
31 | }
32 |
33 | factor_label <- function(x) attr(x, "factor_label")
34 | details <- function(x) attr(x, "details")
35 |
36 | vec_ptype_abbr.tidyrisk_factor <- function(x) {
37 | "r_fctr"
38 | }
39 |
40 | is_tidyrisk_factor <- function(x) {
41 | inherits(x, "tidyrisk_factor")
42 | }
43 |
44 | as_tidyrisk_factor <- function(x, factor_label) {
45 | vec_cast(x, new_tidyrisk_factor(x, factor_label))
46 | }
47 |
48 | # Formatters --------------------------------------------------------------
49 |
50 | #' @importFrom cli cat_line cat_bullet cat_rule cat_print style_bold
51 | #' @importFrom vctrs vec_data
52 | #' @importFrom purrr walk2
53 | format.tidyrisk_factor <- function(x, ...) {
54 | cli::cat_line("# Factor samples: ", length(vctrs::vec_data(x)))
55 | cli::cat_line("# Factor label: ", factor_label(x))
56 | if (length(details(x)) == 0) {
57 | cli::cat_bullet("# Summary details: None.")
58 | } else {
59 | purrr::walk2(names(details(x)), details(x)
60 | ~ cli::cat_bullet("# Summary detail: ", cli::style_bold(.x),
61 | " ", .y))
62 | cli::cat_rule()
63 | }
64 | cli::cat_print(vctrs::vec_data(x))
65 |
66 | invisible(x)
67 | }
68 |
69 | #' @importFrom vctrs vec_data
70 | summary.tidyrisk_factor <- function(object, ...) {
71 | samples <- vctrs::vec_data(object)
72 | switch(factor_label(object),
73 | LM = {
74 | if (length(samples) == 0 || sum(samples, na.rm = TRUE) == 0) {
75 | list(ale = 0, sle_max = 0, sle_min = 0, sle_mean = 0,
76 | sle_median = 0)
77 | } else {
78 | list(ale = sum(samples),
79 | sle_max = max(samples),
80 | sle_min = min(samples[samples > 0]),
81 | sle_mean = mean(samples[samples > 0]),
82 | sle_median = stats::median(samples[samples > 0])
83 | )
84 | }},
85 | list()
86 | )
87 | }
88 |
89 | # Casts -------------------------------------------------------------------
90 |
91 | #' Cast a `tidyrisk_factor` vector to a specified type
92 | #'
93 | #' @inheritParams vctrs::vec_cast
94 | #'
95 | #' @method vec_cast tidyrisk_factor
96 | #' @importFrom vctrs vec_cast
97 | vec_cast.tidyrisk_factor <- function(x, to) UseMethod("vec_cast.tidyrisk_factor")
98 |
99 | #' @method vec_cast.tidyrisk_factor default
100 | #' @importFrom vctrs stop_incompatible_cast
101 | vec_cast.tidyrisk_factor.default <- function(x, to) {
102 | stop_incompatible_cast(x, to)
103 | }
104 |
105 | #' @method vec_cast.tidyrisk_factor class_pred
106 | vec_cast.tidyrisk_factor.class_pred <- function(x, to) {
107 |
108 | # first go tidyrisk_factor -> factor
109 | # then recast as tidyrisk_factor with correct attributes
110 |
111 | tidyrisk_factor(
112 | samples = x,
113 | factor_label = "TF"
114 | )
115 |
116 | }
117 |
118 | # Arithmetic and Comparisons ----------------------------------------------
119 |
120 | #' @importFrom vctrs vec_proxy_equal vec_data
121 | #' @keywords internal
122 | vec_proxy_compare.tidyrisk_factor <- function(x) {
123 | # allows you to compare two class_pred objects robustly
124 | # converting to character would confuse NA with equivocal
125 | vctrs::vec_data(x)
126 | }
127 |
128 | # Misc --------------------------------------------------------------------
129 |
130 | #' Create a tidyrisk_factor sample function
131 | #'
132 | #' @param factor_label abbreviation of the OpenFAIR element
133 | #' @importFrom rlang as_function
134 | tidyrisk_factory <- function(factor_label = "TC") {
135 |
136 | function(.n = 1, ..., .func) {
137 | my_func <- rlang::as_function(.func)
138 | #dots <- enquos(...)
139 |
140 | samples <- my_func(n = .n, ...)
141 | tidyrisk_factor(samples = samples, factor_label = factor_label)
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/tests/testthat/test-validate.R:
--------------------------------------------------------------------------------
1 | context("Validation")
2 | qualitative_scenarios <- data.frame(scenario_id = c("1", "2"),
3 | scenario = c("Scenario A.", "Scenario B."),
4 | tcomm = c("Organizational Leadership", "Organizational Leadership"),
5 | tef = c("frequent", "frequent"),
6 | tc = c("medium", "medium"),
7 | lm = c("medium", "medium"),
8 | domain_id = c("ORG", "ORG"),
9 | controls = c("1, 5, 7, 32, 14",
10 | "14, 15, 16"),
11 | stringsAsFactors = FALSE)
12 | capabilities <- data.frame(capability_id = c("1", "5", "7", "32", "14", "15", "16"),
13 | domain_id = c("ORG", "ORG", "ORG", "ORG", "ORG", "ORG", "ORG"),
14 | capability = c("Capability 1.", "Capability 5.", "Capability 7", "Capability 32.", "Capability 14.", "Capability 15.", "Capability 16."),
15 | diff = c("5 - Optimized", "4 - Managed", "1 - Initial", "4 - Managed", "4 - Managed", "2 - Repeatable", "2 - Repeatable"),
16 | stringsAsFactors = FALSE)
17 | mappings <- data.frame(type = c("tef", "tef", "tef", "tc", "tc", "tc", "diff", "diff", "diff", "diff", "diff", "lm", "lm", "lm"),
18 | label = c("frequent", "occasional", "rare", "high", "medium", "low", "5 - Optimized", "4 - Managed", "3 - Defined", "2 - Repeatable", "1 - Initial", "high", "medium", "low"),
19 | l = c(10L, 1L, 0L, 50L, 33L, 0L, 70L, 50L, 33L, 20L, 0L, 1000000L, 10000L, 100L),
20 | ml = c(24, 6, 0.1, 75, 50, 16, 85, 70, 50, 30, 10, 2e+06, 20000, 200),
21 | h = c(52L, 12L, 1L, 98L, 60L, 30L, 98L, 84L, 60L, 50L, 30L, 5000000L, 500000L, 10000L),
22 | conf = c(4L, 4L, 4L, 3L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 4L, 1L),
23 | stringsAsFactors = FALSE)
24 |
25 | test_that("Duplicate scenarios detected", {
26 | qualitative_scenarios <- data.frame(scenario_id = c("1", "1"),
27 | scenario = c("Scenario A.", "Scenario B."),
28 | tcomm = c("Organizational Leadership", "Organizational Leadership"),
29 | tef = c("frequent", "frequent"),
30 | tc = c("medium", "medium"),
31 | lm = c("medium", "medium"),
32 | domain_id = c("ORG", "ORG"),
33 | controls = c("1, 5, 7, 32, 14",
34 | "14, 15, 16"),
35 | stringsAsFactors = FALSE)
36 | expect_warning(validate_scenarios(qualitative_scenarios, capabilities, domains, mappings),
37 | regexp = "Duplicate scenarios")
38 | })
39 | test_that("Duplicate capabilities detected", {
40 | capabilities <- data.frame(capability_id = c("1", "5", "5", "32", "14", "15", "16"),
41 | domain_id = c("ORG", "ORG", "ORG", "ORG", "ORG", "ORG", "ORG"),
42 | capability = c("Capability 1.", "Capability 5.", "Capability 7", "Capability 32.", "Capability 14.", "Capability 15.", "Capability 16."),
43 | diff = c("5 - Optimized", "4 - Managed", "1 - Initial", "4 - Managed", "4 - Managed", "2 - Repeatable", "2 - Repeatable"),
44 | stringsAsFactors = FALSE)
45 | expect_warning(validate_scenarios(qualitative_scenarios, capabilities, domains, mappings),
46 | regexp = "Duplicate capabilities")
47 | })
48 |
49 |
50 | test_that("Invalid TEF labels are handled", {
51 | data(mc_capabilities, mc_qualitative_scenarios)
52 | bad_scenarios <- mc_qualitative_scenarios
53 | bad_scenarios[1, "tef"] <- "invalid"
54 | expect_warning(validate_scenarios(bad_scenarios, mc_capabilities, domains, mappings),
55 | regexp = "qualitative TEF")
56 |
57 | })
58 |
59 | test_that("Invalid TC labels are handled", {
60 | data(mc_capabilities, mc_qualitative_scenarios)
61 | bad_scenarios <- mc_qualitative_scenarios
62 | bad_scenarios[1, "tc"] <- "invalid"
63 | expect_warning(validate_scenarios(bad_scenarios, mc_capabilities, domains, mappings),
64 | regexp = "qualitative TC")
65 |
66 | })
67 |
68 | test_that("Invalid DIFF labels are handled", {
69 | data(mc_capabilities, mc_qualitative_scenarios)
70 | bad_capabilities <- mc_capabilities
71 | bad_capabilities[1, "diff"] <- "invalid"
72 | expect_warning(validate_scenarios(mc_qualitative_scenarios, bad_capabilities, domains, mappings),
73 | regexp = "qualitative DIFF")
74 |
75 | })
76 |
77 | test_that("Invalid LMs labels are handled", {
78 | data(mc_capabilities, mc_qualitative_scenarios)
79 | bad_scenarios <- mc_qualitative_scenarios
80 | bad_scenarios[1, "lm"] <- "invalid"
81 | expect_warning(validate_scenarios(bad_scenarios, mc_capabilities, domains, mappings),
82 | regexp = "qualitative LM")
83 |
84 | })
85 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # evaluator
5 |
6 |
7 |
8 | [](https://github.com/davidski/evaluator/actions)
10 | [](https://codecov.io/github/davidski/evaluator?branch=master)
12 | [](https://cran.r-project.org/package=evaluator)
13 | 
14 |
15 |
16 | ## Overview
17 |
18 | evaluator is an open source quantitative risk analysis toolkit. Based on
19 | the OpenFAIR [ontology](https://publications.opengroup.org/c20b) and
20 | risk analysis [standard](https://publications.opengroup.org/c20a),
21 | evaluator empowers an organization to perform a quantifiable,
22 | repeatable, and data-driven risk review.
23 |
24 | Three sample outputs of this toolkit are available:
25 |
26 | 1. A [sample risk
27 | analysis](https://evaluator.tidyrisk.org/reports/evaluator_risk_analysis.html)
28 | report
29 | 2. A one page [risk
30 | dashboard](https://evaluator.tidyrisk.org/reports/evaluator_risk_dashboard.html)
31 | 3. A demonstration copy of [Scenario
32 | Explorer](https://davidski.shinyapps.io/scenario_explorer)
33 |
34 | ## Installation
35 |
36 | Install evaluator via the standard CRAN mechanisms. If you wish to use
37 | the optional, but recommended, reporting functions, also install the
38 | suggested dependencies. These additional packages are not needed for
39 | modeling, but are used in the generation of reports.
40 |
41 | ``` r
42 | install.packages("evaluator", dependencies = TRUE)
43 | ```
44 |
45 | If you wish to run the development (and potentially bleeding edge)
46 | version of evaluator, you can install directly from GitHub with the
47 | following `devtools` command.
48 |
49 | ``` r
50 | # install.pacakges("devtools")
51 | devtools::install_github("davidski/evaluator", dependencies = TRUE)
52 | ```
53 |
54 | Optionally, Docker images with all dependencies pre-installed are
55 | available on the [Docker Hub](https://hub.docker.com/r/tidyrisk).
56 |
57 | ## Usage
58 |
59 | The primary workflow for evaluator involves gathering data in Excel then
60 | running the analysis from within the R and Evaluator environment:
61 |
62 | From Excel:
63 |
64 | 1. Populate the evaluator-supplied data acquisition spreadsheet
65 |
66 | From Evaluator:
67 |
68 | 2. Import the data
69 | 3. Prepare the data for simulation
70 | 4. Run the simulations
71 | 5. Summarize the results
72 | 6. Generate draft reports for customization
73 |
74 | A detailed guide is available in the vignette accessed via
75 | `vignette("usage", package="evaluator")`. A short screencast showing the
76 | basic workflow (not including generation of reports) is available below:
77 |
78 | [](https://asciinema.org/a/qIBU3lhPkWHGMYD9O2GU1YgcU?s=2&autoplay=1)
79 |
80 | ## Where to Go from Here
81 |
82 | While evaluator is a powerful tool, it does not attempt to address
83 | interactions between risk scenarios, rolling up multiple levels of risk
84 | into aggregations, or other advanced topics. As you become more
85 | comfortable with quantitative risk analysis, you may wish to dive deeper
86 | into these areas (and I hope you do!). The following resources may help
87 | you explore these and other topics in risk management.
88 |
89 | ### Alternative Software
90 |
91 | - [RiskLens](https://www.risklens.com), a commercial analysis suite,
92 | founded by the original creator of the FAIR methodology
93 | - [FAIR Tool](https://github.com/zugo01/FAIRTool), a Shiny and R based
94 | two scenario simulator, authored by Ezeugo Aguta under an MIT
95 | license
96 | - [FAIR-U](https://www.fairinstitute.org/fair-u), a free educational
97 | tool for learning FAIR analysis, powered by RiskLens
98 | - [Open FAIR Risk Analysis
99 | Tool](https://publications.opengroup.org/i181), an Excel and SIPMath
100 | base tool with a limited open license
101 |
102 | ### Blogs/Books/Training
103 |
104 | - Russell C. Thomas’s excellent and provocative blog post on systemic
105 | [Risk
106 | Management](https://exploringpossibilityspace.blogspot.com/2013/08/risk-management-out-with-old-in-with-new.html)
107 | - [Measuring and Managing Information
108 | Risk](https://smile.amazon.com/gp/product/0124202314)
109 | - [OpenFAIR
110 | certification](https://www.opengroup.org/certifications/openfair)
111 | - [Hubbard Decision Research calibration
112 | training](https://hubbardresearch.com/training/)
113 |
114 | ### Associations
115 |
116 | - [FAIR Institute](https://www.fairinstitute.org)
117 | - [Society of Information Risk Analysts
118 | (SIRA)](https://www.societyinforisk.org/)
119 |
120 | ## Contributing
121 |
122 | This project is governed by a [Code of
123 | Conduct](https://evaluator.tidyrisk.org/CODE_OF_CONDUCT.html). By
124 | participating in this project you agree to abide by these terms.
125 |
126 | ## License
127 |
128 | The [MIT License](LICENSE) applies.
129 |
--------------------------------------------------------------------------------
/README.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | output:
3 | github_document: default
4 | ---
5 |
6 |
7 |
8 | ```{r, echo = FALSE}
9 | knitr::opts_chunk$set(
10 | collapse = TRUE,
11 | comment = "#>",
12 | fig.path = "README-"
13 | )
14 | ```
15 |
16 | # evaluator
17 |
18 |
19 | [](https://github.com/davidski/evaluator/actions)
20 | [](https://codecov.io/github/davidski/evaluator?branch=master)
21 | [](https://cran.r-project.org/package=evaluator)
22 | 
23 |
24 |
25 | ## Overview
26 |
27 | evaluator is an open source quantitative risk analysis toolkit. Based on the OpenFAIR
28 | [ontology](https://publications.opengroup.org/c20b) and
29 | risk analysis [standard](https://publications.opengroup.org/c20a),
30 | evaluator empowers an organization to perform a quantifiable, repeatable, and
31 | data-driven risk review.
32 |
33 | Three sample outputs of this toolkit are available:
34 |
35 | 1) A [sample risk analysis](https://evaluator.tidyrisk.org/reports/evaluator_risk_analysis.html) report
36 | 2) A one page [risk dashboard](https://evaluator.tidyrisk.org/reports/evaluator_risk_dashboard.html)
37 | 3) A demonstration copy of [Scenario Explorer](https://davidski.shinyapps.io/scenario_explorer)
38 |
39 | ## Installation
40 |
41 | Install evaluator via the standard CRAN mechanisms. If you wish to use the
42 | optional, but recommended, reporting functions, also install the
43 | suggested dependencies. These additional packages are not needed for
44 | modeling, but are used in the generation of reports.
45 |
46 | ```{r install_cran, eval=FALSE}
47 | install.packages("evaluator", dependencies = TRUE)
48 | ```
49 |
50 | If you wish to run the development (and potentially bleeding edge) version of
51 | evaluator, you can install directly from GitHub with the following `devtools`
52 | command.
53 |
54 | ```{r github_install, eval=FALSE}
55 | # install.pacakges("devtools")
56 | devtools::install_github("davidski/evaluator", dependencies = TRUE)
57 | ```
58 |
59 | Optionally, Docker images with all dependencies pre-installed are
60 | available on the [Docker Hub](https://hub.docker.com/r/tidyrisk).
61 |
62 | ## Usage
63 |
64 | The primary workflow for evaluator involves gathering data in Excel then
65 | running the analysis from within the R and Evaluator environment:
66 |
67 | From Excel:
68 |
69 | 1. Populate the evaluator-supplied data acquisition spreadsheet
70 |
71 | From Evaluator:
72 |
73 | 2. Import the data
74 | 3. Prepare the data for simulation
75 | 4. Run the simulations
76 | 5. Summarize the results
77 | 6. Generate draft reports for customization
78 |
79 | A detailed guide is available in the vignette accessed via
80 | `vignette("usage", package="evaluator")`. A short screencast showing
81 | the basic workflow (not including generation of reports) is available below:
82 |
83 | [](https://asciinema.org/a/qIBU3lhPkWHGMYD9O2GU1YgcU?s=2&autoplay=1)
84 |
85 | ## Where to Go from Here
86 |
87 | While evaluator is a powerful tool, it does not attempt to address interactions
88 | between risk scenarios, rolling up multiple levels of risk into aggregations, or
89 | other advanced topics. As you become more comfortable with quantitative risk
90 | analysis, you may wish to dive deeper into these areas (and I hope you do!).
91 | The following resources may help you explore these and other topics in risk
92 | management.
93 |
94 | ### Alternative Software
95 |
96 | - [RiskLens](https://www.risklens.com), a commercial analysis suite, founded
97 | by the original creator of the FAIR methodology
98 | - [FAIR Tool](https://github.com/zugo01/FAIRTool), a Shiny and R based two
99 | scenario simulator, authored by Ezeugo Aguta under an MIT license
100 | - [FAIR-U](https://www.fairinstitute.org/fair-u), a free educational tool for
101 | learning FAIR analysis, powered by RiskLens
102 | - [Open FAIR Risk Analysis Tool](https://publications.opengroup.org/i181),
103 | an Excel and SIPMath base tool with a limited open license
104 |
105 | ### Blogs/Books/Training
106 |
107 | - Russell C. Thomas's excellent and provocative blog post on systemic [Risk Management](https://exploringpossibilityspace.blogspot.com/2013/08/risk-management-out-with-old-in-with-new.html)
108 | - [Measuring and Managing Information Risk](https://smile.amazon.com/gp/product/0124202314)
109 | - [OpenFAIR certification](https://www.opengroup.org/certifications/openfair)
110 | - [Hubbard Decision Research calibration training](https://hubbardresearch.com/training/)
111 |
112 | ### Associations
113 |
114 | - [FAIR Institute](https://www.fairinstitute.org)
115 | - [Society of Information Risk Analysts (SIRA)](https://www.societyinforisk.org/)
116 |
117 | ## Contributing
118 |
119 | This project is governed by a
120 | [Code of Conduct](https://evaluator.tidyrisk.org/CODE_OF_CONDUCT.html). By
121 | participating in this project you agree to abide by these terms.
122 |
123 | ## License
124 |
125 | The [MIT License](LICENSE) applies.
126 |
--------------------------------------------------------------------------------
/renv.lock:
--------------------------------------------------------------------------------
1 | {
2 | "R": {
3 | "Version": "4.1.1",
4 | "Repositories": [
5 | {
6 | "Name": "CRAN",
7 | "URL": "https://cran.rstudio.com"
8 | }
9 | ]
10 | },
11 | "Packages": {
12 | "base64enc": {
13 | "Package": "base64enc",
14 | "Version": "0.1-3",
15 | "Source": "Repository",
16 | "Repository": "CRAN",
17 | "Hash": "543776ae6848fde2f48ff3816d0628bc"
18 | },
19 | "cli": {
20 | "Package": "cli",
21 | "Version": "3.0.1",
22 | "Source": "Repository",
23 | "Repository": "CRAN",
24 | "Hash": "e3ae5d68dea0c55a12ea12a9fda02e61"
25 | },
26 | "digest": {
27 | "Package": "digest",
28 | "Version": "0.6.28",
29 | "Source": "Repository",
30 | "Repository": "CRAN",
31 | "Hash": "49b5c6e230bfec487b8917d5a0c77cca"
32 | },
33 | "ellipsis": {
34 | "Package": "ellipsis",
35 | "Version": "0.3.2",
36 | "Source": "Repository",
37 | "Repository": "CRAN",
38 | "Hash": "bb0eec2fe32e88d9e2836c2f73ea2077"
39 | },
40 | "evaluate": {
41 | "Package": "evaluate",
42 | "Version": "0.14",
43 | "Source": "Repository",
44 | "Repository": "CRAN",
45 | "Hash": "ec8ca05cffcc70569eaaad8469d2a3a7"
46 | },
47 | "fastmap": {
48 | "Package": "fastmap",
49 | "Version": "1.1.0",
50 | "Source": "Repository",
51 | "Repository": "CRAN",
52 | "Hash": "77bd60a6157420d4ffa93b27cf6a58b8"
53 | },
54 | "glue": {
55 | "Package": "glue",
56 | "Version": "1.4.2",
57 | "Source": "Repository",
58 | "Repository": "CRAN",
59 | "Hash": "6efd734b14c6471cfe443345f3e35e29"
60 | },
61 | "highr": {
62 | "Package": "highr",
63 | "Version": "0.9",
64 | "Source": "Repository",
65 | "Repository": "CRAN",
66 | "Hash": "8eb36c8125038e648e5d111c0d7b2ed4"
67 | },
68 | "htmltools": {
69 | "Package": "htmltools",
70 | "Version": "0.5.2",
71 | "Source": "Repository",
72 | "Repository": "CRAN",
73 | "Hash": "526c484233f42522278ab06fb185cb26"
74 | },
75 | "jquerylib": {
76 | "Package": "jquerylib",
77 | "Version": "0.1.4",
78 | "Source": "Repository",
79 | "Repository": "CRAN",
80 | "Hash": "5aab57a3bd297eee1c1d862735972182"
81 | },
82 | "jsonlite": {
83 | "Package": "jsonlite",
84 | "Version": "1.7.2",
85 | "Source": "Repository",
86 | "Repository": "CRAN",
87 | "Hash": "98138e0994d41508c7a6b84a0600cfcb"
88 | },
89 | "knitr": {
90 | "Package": "knitr",
91 | "Version": "1.36",
92 | "Source": "Repository",
93 | "Repository": "CRAN",
94 | "Hash": "46344b93f8854714cdf476433a59ed10"
95 | },
96 | "magrittr": {
97 | "Package": "magrittr",
98 | "Version": "2.0.1",
99 | "Source": "Repository",
100 | "Repository": "CRAN",
101 | "Hash": "41287f1ac7d28a92f0a286ed507928d3"
102 | },
103 | "purrr": {
104 | "Package": "purrr",
105 | "Version": "0.3.4",
106 | "Source": "Repository",
107 | "Repository": "CRAN",
108 | "Hash": "97def703420c8ab10d8f0e6c72101e02"
109 | },
110 | "remotes": {
111 | "Package": "remotes",
112 | "Version": "2.4.1",
113 | "Source": "Repository",
114 | "Repository": "CRAN",
115 | "Hash": "feaca31e417db79fd1832e25b51a7717"
116 | },
117 | "renv": {
118 | "Package": "renv",
119 | "Version": "0.13.2",
120 | "Source": "Repository",
121 | "Repository": "CRAN",
122 | "Hash": "079cb1f03ff972b30401ed05623cbe92"
123 | },
124 | "rlang": {
125 | "Package": "rlang",
126 | "Version": "0.4.11",
127 | "Source": "Repository",
128 | "Repository": "CRAN",
129 | "Hash": "515f341d3affe0de9e4a7f762efb0456"
130 | },
131 | "rmarkdown": {
132 | "Package": "rmarkdown",
133 | "Version": "2.11",
134 | "Source": "Repository",
135 | "Repository": "CRAN",
136 | "Hash": "320017b52d05a943981272b295750388"
137 | },
138 | "rstudioapi": {
139 | "Package": "rstudioapi",
140 | "Version": "0.13",
141 | "Source": "Repository",
142 | "Repository": "CRAN",
143 | "Hash": "06c85365a03fdaf699966cc1d3cf53ea"
144 | },
145 | "stringi": {
146 | "Package": "stringi",
147 | "Version": "1.7.5",
148 | "Source": "Repository",
149 | "Repository": "CRAN",
150 | "Hash": "cd50dc9b449de3d3b47cdc9976886999"
151 | },
152 | "stringr": {
153 | "Package": "stringr",
154 | "Version": "1.4.0",
155 | "Source": "Repository",
156 | "Repository": "CRAN",
157 | "Hash": "0759e6b6c0957edb1311028a49a35e76"
158 | },
159 | "tinytex": {
160 | "Package": "tinytex",
161 | "Version": "0.34",
162 | "Source": "Repository",
163 | "Repository": "CRAN",
164 | "Hash": "043daa786f4d254f0031534150e28d42"
165 | },
166 | "vctrs": {
167 | "Package": "vctrs",
168 | "Version": "0.3.8",
169 | "Source": "Repository",
170 | "Repository": "CRAN",
171 | "Hash": "ecf749a1b39ea72bd9b51b76292261f1"
172 | },
173 | "xfun": {
174 | "Package": "xfun",
175 | "Version": "0.26",
176 | "Source": "Repository",
177 | "Repository": "CRAN",
178 | "Hash": "a270216f7ffda25e53298293046d1d05"
179 | },
180 | "yaml": {
181 | "Package": "yaml",
182 | "Version": "2.2.1",
183 | "Source": "Repository",
184 | "Repository": "CRAN",
185 | "Hash": "2826c5d9efb0a88f657c7a679c7106db"
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/inst/openfair_example/openfair_example.Rmd:
--------------------------------------------------------------------------------
1 | ---
2 | title: "OpenFAIR Example"
3 | runtime: shiny
4 | output:
5 | flexdashboard::flex_dashboard:
6 | orientation: columns
7 | vertical_layout: fill
8 | source_code: embed
9 | navbar:
10 | - { title: "About", href: "https://evaluator.tidyrisk.org", align: right }
11 | ---
12 |
13 | ```{r setup, include=FALSE}
14 | library(dplyr)
15 | library(evaluator)
16 | library(flexdashboard)
17 | library(ggplot2)
18 | library(mc2d)
19 | library(scales)
20 | library(shiny)
21 | ```
22 |
23 | ```{r common_functions, include=FALSE}
24 | ```
25 |
26 | ```{r core_functions, include=FALSE}
27 | # determine the proper base font to use for graphs
28 | basefont <- get_base_fontfamily()
29 |
30 | values <- reactiveValues()
31 |
32 | values$iterations <- 1000
33 |
34 | observeEvent(input$runmodel, {
35 |
36 | values$iterations <- input$iterations
37 |
38 | TEFestimate <- list(func = "mc2d::rpert", min = input$tefl,
39 | mode = input$tefml, max = input$tefh,
40 | shape = input$tefconf)
41 | TCestimate <- list(func = "mc2d::rpert", min = input$tcapl,
42 | mode = input$tcapml, max = input$tcaph,
43 | shape = input$tcapconf)
44 | DIFFestimate <- list(list(func = "mc2d::rpert", min = input$csl,
45 | mode = input$csml, max = input$csh,
46 | shape = input$csconf))
47 | LMestimate <- list(func = "mc2d::rpert", min = input$lml,
48 | mode = input$lmml, max = input$lmh,
49 | shape = input$lmconf)
50 | single_scen <- tidyrisk_scenario(
51 | tef_params = TEFestimate,
52 | tc_params = TCestimate,
53 | diff_params = DIFFestimate,
54 | lm_params = LMestimate)
55 |
56 | values$simulation_result <- run_simulation(single_scen, iterations = values$iterations)
57 |
58 | })
59 | ```
60 |
61 | Scenario Parameters {.sidebar data-width=400}
62 | -----------------------------------------------------------------------
63 |
64 | ### TEF
65 |
66 | Frequency of action (events per year) by the actor.
67 |
68 | ```{r input_tef}
69 | fillRow(height = 50,
70 | numericInput("tefl", "Min:", 10, min = 0, max = 100),
71 | numericInput("tefml", "ML:", 20, min = 0, max = 100),
72 | numericInput("tefh", "Max:", 100, min = 0, max = 100),
73 | numericInput("tefconf", "Shape:", 1, min = 1, max = 5)
74 | )
75 | ```
76 |
77 | ### TC
78 |
79 | Capabilities (in %) of the threat actor.
80 |
81 | ```{r input_tcap}
82 | fillRow(height = 50,
83 | numericInput("tcapl", "Min:", .20, min = 0, max = 1, step = 0.01),
84 | numericInput("tcapml", "ML:", .30, min = 0, max = 1, step = 0.01),
85 | numericInput("tcaph", "Max:", .70, min = 0, max = 1, step = 0.01),
86 | numericInput("tcapconf", "Shape:", 1, min = 1, max = 5)
87 | )
88 | ```
89 |
90 | ### DIFF
91 |
92 | Difficulty (in %) presented by the controls.
93 |
94 | ```{r input_diff}
95 | fillRow(height = 50,
96 | numericInput("csl", "Min:", .40, min = 0, max = 1, step = 0.01),
97 | numericInput("csml", "ML:", .50, min = 0, max = 1, step = 0.01),
98 | numericInput("csh", "Max:", .60, min = 0, max = 1, step = 0.01),
99 | numericInput("csconf", "Shape:", 2, min = 1, max = 5)
100 | )
101 | ```
102 |
103 | ### LM
104 |
105 | Total losses (in $) for a single adverse event.
106 |
107 | ```{r input_lm}
108 | fillRow(height = 50,
109 | numericInput("lml", "Min:", 100, min = 0, step = 0.01),
110 | numericInput("lmml", "ML:", 500, min = 0, step = 0.01),
111 | numericInput("lmh", "Max:", 10000, min = 0, step = 0.01),
112 | numericInput("lmconf", "Shape:", 1, min = 1, max = 5)
113 | )
114 | ```
115 |
116 | ### Params {.no-title}
117 |
118 | ```{r}
119 | numericInput("iterations", "# Iterations:", 100, min = 10, max = 10000,
120 | step = 100)
121 | actionButton("runmodel", "Run Model")
122 | ```
123 |
124 | Results {.tabset data-width=600}
125 | ----------------------------------------------------------------------
126 |
127 | ### Loss Distribution
128 |
129 | ```{r plot_distribution}
130 | renderPlot({
131 | if (input$runmodel != 0) {
132 | gg <- ggplot(values$simulation_result, aes(x = ale))
133 | gg <- gg + geom_histogram(binwidth = diff(range(values$simulation_result$ale) / 50),
134 | aes(y = ..density..),
135 | color = "black",
136 | fill = "white")
137 | gg <- gg + geom_density(fill = "steelblue", alpha = 1/3)
138 | gg <- gg + scale_x_continuous(labels = comma)
139 | gg <- gg + labs(x = "Annual Expected Losses")
140 | gg <- gg + theme_evaluator(base_family = basefont)
141 | print(gg)
142 | }
143 | })
144 | ```
145 |
146 | ### Details
147 |
148 | #### Loss Summary {.no-title}
149 |
150 | ```{r detail1}
151 | renderPrint({
152 | if (input$runmodel != 0) {
153 | print(summary(values$simulation_result$ale))
154 | }
155 | })
156 | ```
157 |
158 | #### 95% Value at Risk {.no-title}
159 |
160 | ```{r detail2}
161 | renderText({
162 | if (input$runmodel != 0) {
163 | VAR <- summarize_scenario(values$simulation_result) %>% pull(ale_var)
164 | print(paste0("Losses at 95th percentile are $",
165 | format(VAR, nsmall = 2, big.mark = ",")
166 | ))
167 | }
168 | })
169 | ```
170 |
171 | #### Scenario Result Summary {.no-title}
172 |
173 | ```{r detail3}
174 | renderTable({
175 | if (input$runmodel != 0) {
176 | summarize_scenario(values$simulation_result)
177 | }
178 | })
179 | ```
180 |
181 | #### Loss Samples {data-height=200}
182 |
183 | ```{r detail4}
184 | DT::renderDataTable(
185 | if (input$runmodel != 0) {
186 | dat <- data.frame(Losses = values$simulation_result$ale) %>%
187 | arrange(desc(Losses)) %>%
188 | transmute(Losses = scales::dollar(Losses))
189 | DT::datatable(dat, rownames = FALSE)
190 | }
191 | )
192 | ```
193 |
194 | ### Event Distribution
195 |
196 | ```{r loss_events}
197 | renderPlot({
198 | if (input$runmodel != 0) {
199 | VULNsamples <- values$simulation_result %>%
200 | summarize(loss_events = sum(loss_events, na.rm = TRUE),
201 | avoided_events = sum(threat_events, na.rm = TRUE) - loss_events) %>%
202 | tidyr::gather("key", "value", loss_events, avoided_events) %>%
203 | mutate(value = value / sum(value, na.rm = TRUE))
204 | gg <- ggplot(VULNsamples, aes(x = key, y = value)) +
205 | geom_col(fill = "steelblue") +
206 | labs(x = "Event Type", y = "Percent") +
207 | scale_y_continuous(labels = scales::percent) +
208 | theme_evaluator(base_family = evaluator::get_base_fontfamily())
209 | print(gg)
210 | }
211 | })
212 | ```
213 |
--------------------------------------------------------------------------------
/R/data.R:
--------------------------------------------------------------------------------
1 | #' Capabilities
2 | #'
3 | #' A sample set of capabilities for the demonstration (and artificial)
4 | #' MetroCare information security program.
5 | #'
6 | #' @source
7 | #' This is hypothetical information. Any similarity to any other
8 | #' entity is completely coincidental.
9 | #'
10 | #' @format
11 | #' \describe{
12 | #' \item{capability_id}{unique id of the capability}
13 | #' \item{domain_id}{domain id to which the capability applies}
14 | #' \item{capability}{full text summary of the capability}
15 | #' \item{diff}{qualitative label of control effectiveness}
16 | #' }
17 | "mc_capabilities"
18 |
19 | #' Qualitative to quantitative mappings
20 | #'
21 | #' A sample set of qualitative to quantitative mappings for the demonstration
22 | #' (and artificial) MetroCare information security program.
23 | #'
24 | #' @source
25 | #' This is hypothetical information. Any similarity to any other
26 | #' entity is completely coincidental.
27 | #'
28 | #' @format
29 | #' \describe{
30 | #' \item{type}{The element in the OpenFAIR ontology to which this mapping applies}
31 | #' \item{label}{Qualitative label}
32 | #' \item{l}{BetaPERT low value}
33 | #' \item{ml}{BetaPERT most likely value}
34 | #' \item{h}{BetaPERT high value}
35 | #' \item{conf}{BetaPERT confidence value}
36 | #' }
37 | "mc_mappings"
38 |
39 | #' Domain mappings
40 | #'
41 | #' A sample set of the domains for the demonstration (and artificial)
42 | #' MetroCare information security program.
43 | #'
44 | #' @source
45 | #' This is hypothetical information. Any similarity to any other
46 | #' entity is completely coincidental.
47 | #'
48 | #' @format
49 | #' \describe{
50 | #' \item{domain_id}{abbreviated name of the domain}
51 | #' \item{domain}{full title of the domain}
52 | #' }
53 | "mc_domains"
54 |
55 | #' Domain-level risk summary
56 | #'
57 | #' A sample set of quantified information security risk exposure, summarized
58 | #' at the domain level, for the demonstration (and artificial)
59 | #' MetroCare information security program.
60 | #'
61 | #' @source
62 | #' This is hypothetical information. Any similarity to any other
63 | #' entity is completely coincidental.
64 | #'
65 | #' @format
66 | #' \describe{
67 | #' \item{domain_id}{abbreviated name of the domain}
68 | #' \item{loss_events_mean}{mean number of loss events}
69 | #' \item{loss_events_min}{minimum number of loss events}
70 | #' \item{loss_events_max}{maximum number of loss events}
71 | #' \item{loss_events_median}{median number of loss events}
72 | #' \item{ale_max}{minimum annual loss expected}
73 | #' \item{ale_median}{median annual loss expected}
74 | #' \item{ale_mean}{mean annual loss expected}
75 | #' \item{ale_max}{maximum annual loss expected}
76 | #' \item{ale_sd}{standard deviation annual loss expected}
77 | #' \item{ale_var}{value at risk, ale}
78 | #' \item{mean_threat_events}{mean threat events}
79 | #' \item{mean_avoided_events}{mean avoided events}
80 | #' \item{mean_tc_exceedance}{mean threat capability exceedance}
81 | #' \item{mean_diff_exceedance}{mean difficulty exceedance}
82 | #' \item{mean_vuln}{mean vulnerability of the scenario}
83 | #' }
84 | "mc_domain_summary"
85 |
86 | #' Scenario-level risk summary
87 | #'
88 | #' A sample set of quantified information security risk exposure, summarized
89 | #' at the scenario level, for the demonstration (and artificial)
90 | #' MetroCare information security program.
91 | #'
92 | #' @source
93 | #' This is hypothetical information. Any similarity to any other
94 | #' entity is completely coincidental.
95 | #'
96 | #' @format
97 | #' \describe{
98 | #' \item{scenario_id}{ID of the scenario}
99 | #' \item{domain_id}{domain id}
100 | #' \item{control_description}{control description}
101 | #' \item{results}{nested data frame of simulation results for the scenario}
102 | #' \item{loss_events_mean}{mean number of loss events}
103 | #' \item{loss_events_median}{median number of loss events}
104 | #' \item{loss_events_min}{minimum number of loss events}
105 | #' \item{loss_events_max}{maximum number of loss events}
106 | #' \item{ale_median}{median annual loss expected}
107 | #' \item{ale_max}{maximum annual loss expected}
108 | #' \item{ale_var}{value at risk, ale}
109 | #' \item{sle_min}{minimum single loss expectancy}
110 | #' \item{sle_max}{maximum single loss expectancy}
111 | #' \item{sle_mean}{mean single loss expectancy}
112 | #' \item{sle_median}{median single loss expectancy}
113 | #' \item{mean_tc_exceedance}{mean threat capability exceedance}
114 | #' \item{mean_diff_exceedance}{mean difficulty exceedance}
115 | #' \item{mean_vuln}{mean vulnerability of the scenario}
116 | #' }
117 | "mc_scenario_summary"
118 |
119 | #' Quantified information risk scenarios
120 | #'
121 | #' A sample set of quantified information security risk scenarios for the
122 | #' demonstration (and artificial) MetroCare information security program.
123 | #'
124 | #' @source
125 | #' This is hypothetical information. Any similarity to any other
126 | #' entity is completely coincidental.
127 | #'
128 | #' @format
129 | #' A dataset of quantified risk scenarios, with parameters
130 | #' describing the distribution of each input.
131 | #'
132 | #' \describe{
133 | #' \item{scenario_id}{id of the scenario, primary key}
134 | #' \item{scenario_description}{full text description of the risk scenario}
135 | #' \item{tcomm}{description of the threat community}
136 | #' \item{domain_id}{domain id}
137 | #' \item{control_descriptons}{named list of the text description of controls involved}
138 | #' \item{scenario}{\code{\link{tidyrisk_scenario}} objects}
139 | #' }
140 | "mc_quantitative_scenarios"
141 |
142 | #' Qualitative information security risk scenarios
143 | #'
144 | #' A sample set of qualitative information security risk scenarios for the
145 | #' demonstration (and artificial) MetroCare information security program.
146 | #'
147 | #' No connection with any other similarly named entity is intended or implied.
148 | #'
149 | #' @source
150 | #' This is hypothetical information. Any similarity to any other
151 | #' entity is completely coincidental.
152 | #' @format
153 | #' \describe{
154 | #' \item{scenario_id}{id of the scenario, primary key}
155 | #' \item{scenario}{full text description of the risk scenario}
156 | #' \item{tcomm}{full text name of threat community}
157 | #' \item{tef}{qualitative label of threat frequency}
158 | #' \item{tc}{qualitative label of threat capability}
159 | #' \item{lm}{qualitative label of loss magnitude}
160 | #' \item{domain_id}{domain id}
161 | #' \item{controls}{comma delimited list of controls ids}
162 | #' }
163 | "mc_qualitative_scenarios"
164 |
165 | #' Simulation results
166 | #'
167 | #' A sample set of information security risk scenario simulation results
168 | #' for the demonstration (and artificial) MetroCare information security
169 | #' program.
170 | #'
171 | #' @source
172 | #' This is hypothetical information. Any similarity to any other
173 | #' entity is completely coincidental.
174 | #'
175 | #' @format
176 | #' \describe{
177 | #' \item{scenario_id}{id of the scenario}
178 | #' \item{domain_id}{domain id}
179 | #' \item{results}{nested data frame of simulation results for the scenario}
180 | #' }
181 | "mc_simulation_results"
182 |
--------------------------------------------------------------------------------