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 @@ -12,3 +12,8 @@
**Vulnerability:** The code uses `readline()` in `R/aFIPC.R` to prompt the user. In non-interactive environments (e.g., CI/CD, automation scripts, agents), this can block execution or leave the process consuming resources.
**Learning:** `readline()` should only be used when `interactive()` is true. Interactive prompts are insecure and unstable in automated contexts because they block execution unconditionally.
**Prevention:** Always wrap `readline()` logic in an `if (interactive())` check or provide sensible defaults for non-interactive execution.

## 2024-06-30 - Prevent Infinite Loop DoS in User Prompts
**Vulnerability:** The code used `readline()` inside an unbounded `while` loop to validate user inputs. In environments where standard input is continuously piped or automated incorrectly, `readline()` repeatedly returns invalid inputs (like `""`), causing the loop to run infinitely. This leads to 100% CPU utilization and eventual service denial (DoS).
**Learning:** Even when `interactive()` safeguards are present, user-provided loops over `readline()` are dangerous without an upper bound, as inputs can still be automated or maliciously piped in interactive-like environments.
**Prevention:** Always include a `max_retries` counter (e.g., 5) and break or `stop()` when the limit is reached to prevent unbounded looping.
12 changes: 12 additions & 0 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,12 @@ autoFIPC <-
)
}

max_retries <- 5
retries <- 0
n <- readline(prompt = "Is it correct? (1: Yes 2: No) : ")
while (!grepl("^[0-9]+$", n)) {
retries <- retries + 1
if (retries >= max_retries) stop("Too many invalid inputs, preventing infinite loop.")
n <- readline(prompt = "Is it correct? (1: Yes 2: No) : ")
}

Expand All @@ -115,11 +119,15 @@ autoFIPC <-
if (itemtype == '3PL' && length(oldformBILOGprior) == 0) {
checkoldformBILOGprior <- function() {
if (!interactive()) return(1L)
max_retries <- 5
retries <- 0
n <-
readline(
prompt = "Do you want to use default BILOG-MG priors for oldform Data? (1: Yes 2: No) : "
)
while (!grepl("^[0-9]+$", n)) {
retries <- retries + 1
if (retries >= max_retries) stop("Too many invalid inputs, preventing infinite loop.")
n <-
readline(
prompt = "Do you want to use default BILOG-MG priors for oldform Data? (1: Yes 2: No) : "
Expand Down Expand Up @@ -330,11 +338,15 @@ autoFIPC <-
if (itemtype == '3PL' && length(newformBILOGprior) == 0) {
checknewformBILOGprior <- function() {
if (!interactive()) return(1L)
max_retries <- 5
retries <- 0
n <-
readline(
prompt = "Do you want to use default BILOG-MG priors for newform Data? (1: Yes 2: No) : "
)
while (!grepl("^[0-9]+$", n)) {
retries <- retries + 1
if (retries >= max_retries) stop("Too many invalid inputs, preventing infinite loop.")
n <-
readline(
prompt = "Do you want to use default BILOG-MG priors for newform Data? (1: Yes 2: No) : "
Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-security.R
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
test_that("autoFIPC handles non-interactive sessions securely to prevent DoS (checkCorrect)", {
expect_error(
autoFIPC(
aFIPC::autoFIPC(
newformXData = matrix(0, nrow=10, ncol=10),
oldformYData = matrix(0, nrow=10, ncol=10),
newformCommonItemNames = c("Item1"),
Expand Down
Loading