Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@
**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.

## 2024-07-13 - [Strict Input Validation on Interactive Prompts]
**Vulnerability:** Unhandled coercion NA exception due to weak integer input validation (`^[0-9]+$`). If a user inputs a massive string (e.g., `"99999999999"`) into `readline()`, `as.integer()` coerces it to `NA`, which then crashes the `if` checks, causing a Denial of Service.
**Learning:** R does not inherently limit input string length in `readline()` nor does `as.integer()` catch out-of-bound overflow gracefully without introducing an `NA`. Checking for merely `[0-9]+` allows oversized integers.
**Prevention:** Use strictly bounded exact-match regex like `^[12]$` when reading constrained inputs to ensure coercion functions like `as.integer()` never encounter out-of-bound, `NA`-producing values.
9 changes: 6 additions & 3 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ autoFIPC <-
}
for (attempt in seq_len(3)) {
n <- readline(prompt = "Is it correct? (1: Yes 2: No) : ")
if (grepl("^[0-9]+$", n)) {
# Security: Strict input validation to prevent coercion crashes
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down Expand Up @@ -171,7 +172,8 @@ autoFIPC <-
readline(
prompt = "Do you want to use default BILOG-MG priors for oldform Data? (1: Yes 2: No) : "
)
if (grepl("^[0-9]+$", n)) {
# Security: Strict input validation to prevent coercion crashes
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down Expand Up @@ -390,7 +392,8 @@ autoFIPC <-
readline(
prompt = "Do you want to use default BILOG-MG priors for newform Data? (1: Yes 2: No) : "
)
if (grepl("^[0-9]+$", n)) {
# Security: Strict input validation to prevent coercion crashes
if (grepl("^[12]$", n)) {
return(as.integer(n))
}
}
Expand Down
21 changes: 21 additions & 0 deletions tests/testthat/test-sentinel-validation.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
library(mockery)

test_that("autoFIPC validates boolean flags for newformBILOGprior, oldformBILOGprior, and confirmCommonItems", {
# newformBILOGprior
expect_error(
Expand Down Expand Up @@ -35,3 +37,22 @@ test_that("autoFIPC validates boolean flags for newformBILOGprior, oldformBILOGp
"Security Error: confirmCommonItems must be a single non-NA logical value or NULL"
)
})

test_that("autoFIPC validates interactive common item correctly with mocks", {
# Mock interactive to TRUE to test the readline loops
mockery::stub(aFIPC::autoFIPC, 'interactive', TRUE)

# Stub readline to return an invalid option '3' 3 times, which should exhaust attempts
mockery::stub(aFIPC::autoFIPC, 'readline', mockery::mock('3', '3', '3'))

expect_error(
aFIPC::autoFIPC(
newformXData = data.frame(A=1),
oldformYData = data.frame(A=2),
newformCommonItemNames = c('A'),
oldformCommonItemNames = c('A'),
confirmCommonItems = NULL
),
"Too many invalid common item confirmation attempts"
)
})
Loading