diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a8207a48..02985d61 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -1,4 +1,4 @@ ## 2024-07-12 - Fix missing parameter validations **Vulnerability:** Unvalidated inputs passed to `if()` statements can cause process crashes (`condition has length > 1`) or unexpected coercion vulnerabilities. **Learning:** In R, optional boolean parameters that default to `NULL` should be validated using explicit runtime type validation (e.g., `if (!is.null(flag) && (!is.logical(flag) || length(flag) != 1 || is.na(flag)))`). -**Prevention:** Always implement explicit runtime type validation for optional boolean parameters. +**Prevention:** Always implement explicit runtime type validation for optional boolean parameters. \ No newline at end of file diff --git a/R/aFIPC.R b/R/aFIPC.R index 62546519..918e19b1 100644 --- a/R/aFIPC.R +++ b/R/aFIPC.R @@ -141,7 +141,7 @@ autoFIPC <- } for (attempt in seq_len(3)) { n <- readline(prompt = "Is it correct? (1: Yes 2: No) : ") - if (grepl("^[0-9]+$", n)) { + if (grepl("^[12]$", n)) { return(as.integer(n)) } } @@ -171,7 +171,7 @@ autoFIPC <- readline( prompt = "Do you want to use default BILOG-MG priors for oldform Data? (1: Yes 2: No) : " ) - if (grepl("^[0-9]+$", n)) { + if (grepl("^[12]$", n)) { return(as.integer(n)) } } @@ -390,7 +390,7 @@ autoFIPC <- readline( prompt = "Do you want to use default BILOG-MG priors for newform Data? (1: Yes 2: No) : " ) - if (grepl("^[0-9]+$", n)) { + if (grepl("^[12]$", n)) { return(as.integer(n)) } } diff --git a/tests/testthat/test-readline-validation.R b/tests/testthat/test-readline-validation.R new file mode 100644 index 00000000..7cbea4a1 --- /dev/null +++ b/tests/testthat/test-readline-validation.R @@ -0,0 +1,46 @@ +make_interactive_auto_fipc <- function(responses) { + prompt_count <- 0L + subject <- autoFIPC + overrides <- new.env(parent = environment(subject)) + overrides$interactive <- function() TRUE + overrides$readline <- function(prompt = "") { + prompt_count <<- prompt_count + 1L + responses[[prompt_count]] + } + environment(subject) <- overrides + + list( + run = function() { + subject( + newformXData = matrix(c(0, 1), ncol = 1), + oldformYData = matrix(c(0, 1), ncol = 1), + newformCommonItemNames = "item1", + oldformCommonItemNames = "item1", + itemtype = "2PL" + ) + }, + prompt_count = function() prompt_count + ) +} + +test_that("autoFIPC rejects non-menu numeric input before integer coercion", { + harness <- make_interactive_auto_fipc(c("3", "99999999999999999999", "10")) + + expect_error( + harness$run(), + "Too many invalid common item confirmation attempts", + fixed = TRUE + ) + expect_equal(harness$prompt_count(), 3L) +}) + +test_that("autoFIPC accepts the exact menu choice 2", { + harness <- make_interactive_auto_fipc("2") + + expect_error( + harness$run(), + "Please write down pairs correctly", + fixed = TRUE + ) + expect_equal(harness$prompt_count(), 1L) +})