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-18 - Fix Integer Overflow Coercion in readline Input Validation
**Vulnerability:** In `R/aFIPC.R`, the application used a permissive regex `^[0-9]+$` to validate numeric interactive inputs from `readline()` before coercing them with `as.integer()`. An attacker could input an excessively long numeric string (e.g., `999999999999999999999`), which bypasses the regex check but evaluates to `NA` during coercion, leading to an unexpected state or subsequent process crashes due to integer overflow.
**Learning:** R's `as.integer()` fails silently with a warning and returns `NA` when given numbers exceeding the 32-bit integer limits, making broad regex checks like `^[0-9]+$` insufficient for safe parsing of integer inputs intended for program control flow.
**Prevention:** Always match against the specific, finite set of expected valid inputs (e.g., `^[12]$`) when validating numerical inputs from `readline()` before parsing, preventing integer overflow and logic bypass vulnerabilities.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Description: Automates fixed item parameter linking for test linking under
the item response theory paradigm using mirt package estimates.
License: GPL-3 | file LICENSE
Imports: mirt, methods
Suggests: testthat (>= 3.0.0)
Suggests: testthat (>= 3.0.0), mockery
Encoding: UTF-8
Config/testthat/edition: 3
Config/roxygen2/version: 8.0.0
6 changes: 3 additions & 3 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
Expand Down Expand Up @@ -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))
}
}
Expand Down Expand Up @@ -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))
}
}
Expand Down
41 changes: 41 additions & 0 deletions tests/testthat/test-sentinel-validation.R
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,44 @@ 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 integer overflow in interactive prompt validation", {
# Mock interactive environment and multiple invalid readline inputs
mock_interactive <- mockery::mock(TRUE, cycle = TRUE)
mockery::stub(aFIPC::autoFIPC, "interactive", mock_interactive)

# Check validation for checkCorrect() common items prompt
mock_readline_common <- mockery::mock("999999999999999999", "abc", "0", cycle = TRUE)
mockery::stub(aFIPC::autoFIPC, "readline", mock_readline_common)

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"
)

# Validate interactive readline validation accepts 1
mock_readline_success <- mockery::mock("1", "1", "1", cycle = TRUE)

mockery::stub(aFIPC::autoFIPC, "interactive", mock_interactive)
mockery::stub(aFIPC::autoFIPC, "readline", mock_readline_success)

# For oldformBILOGprior interactive prompt
expect_error(
aFIPC::autoFIPC(
newformXData = data.frame(A=c(1,0,1,0)),
oldformYData = data.frame(A=c(1,1,0,0)),
newformCommonItemNames = c('A'),
oldformCommonItemNames = c('A'),
confirmCommonItems = NULL,
itemtype = '3PL',
oldformBILOGprior = NULL
),
"Security Error: oldformYData must be a data.frame, matrix, or a valid fitted mirt model|Security Error: Initial estimation of oldFormModel completely failed|Common Items are not equal|Security Error: itemtype must be length|The following items have only one response category|Too few degrees of freedom"
)
})
Loading