diff --git a/.jules/sentinel.md b/.jules/sentinel.md deleted file mode 100644 index bc5c6e1..0000000 --- a/.jules/sentinel.md +++ /dev/null @@ -1,14 +0,0 @@ -## 2024-05-24 - Fix Information Disclosure in try() -**Vulnerability:** `try()` block in `llcont.R` defaulted to `silent = FALSE`, inadvertently leaking internal execution errors (e.g., matrix singularity details) to standard error. -**Learning:** R's `try()` defaults to printing errors unless `silent = TRUE` is explicitly provided. -**Prevention:** Always use `silent = TRUE` inside `try()` blocks or prefer `tryCatch()` to gracefully handle exceptions and prevent information disclosure. - -## 2024-05-25 - Fix Information Disclosure in error handling -**Vulnerability:** Error handling in vuongtest used stop with the raw error object, which can expose internal execution details and call stacks. -**Learning:** Re-throwing errors directly propagates the entire condition object including the call, which can leak internal arguments and stack trace details. -**Prevention:** Always use stop with call. = FALSE and a custom generic message to prevent information disclosure. - -## 2024-07-13 - Prevent Information Disclosure in all stop/warning calls -**Vulnerability:** Raw `stop()` and `warning()` calls without `call. = FALSE` in `llcont.R` and `vuongtest.R` exposed execution stack/call details when raised. -**Learning:** While some instances of `stop()` inside `tryCatch()` were previously fixed to hide the call stack, other standalone exceptions and warnings still leaked call context. Security must be consistently applied across the entire codebase. -**Prevention:** Always set `call. = FALSE` when using `stop()` or `warning()` to enforce a secure-by-default boundary and prevent internal execution paths from being disclosed to the end user. diff --git a/R/icci.R b/R/icci.R index f22278a..15ea7bb 100644 --- a/R/icci.R +++ b/R/icci.R @@ -65,6 +65,10 @@ #' @export icci <- function(object1, object2, conf.level=.95, ll1=llcont, ll2=llcont) { + if (!is.numeric(conf.level) || length(conf.level) != 1 || is.na(conf.level) || conf.level <= 0 || conf.level >= 1) { + stop("conf.level must be a single numeric value between 0 and 1.", call. = FALSE) + } + ## check objects, issue warnings/errors, get classes/calls obinfo <- check.obj(object1, object2) callA <- obinfo$callA; classA <- obinfo$classA diff --git a/tests/testthat/test_icci_conf_level.R b/tests/testthat/test_icci_conf_level.R new file mode 100644 index 0000000..69cec96 --- /dev/null +++ b/tests/testthat/test_icci_conf_level.R @@ -0,0 +1,13 @@ +context("icci_conf_level") + +test_that("icci throws error for invalid conf.level", { + library(MASS) + house1 <- glm(Freq ~ Infl + Type + Cont, family=poisson, data=housing) + house2 <- glm(Freq ~ Infl + Sat, family=poisson, data=housing) + + expect_error(icci(house2, house1, conf.level = "invalid"), "conf.level must be a single numeric value between 0 and 1.") + expect_error(icci(house2, house1, conf.level = -0.5), "conf.level must be a single numeric value between 0 and 1.") + expect_error(icci(house2, house1, conf.level = 1.5), "conf.level must be a single numeric value between 0 and 1.") + expect_error(icci(house2, house1, conf.level = c(0.95, 0.99)), "conf.level must be a single numeric value between 0 and 1.") + expect_error(icci(house2, house1, conf.level = NA), "conf.level must be a single numeric value between 0 and 1.") +})