From 200d1feb708ce48cd8525eb6dffcd7c92ff93e6d Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:09:38 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=EB=B3=B4=EC=95=88=20=EA=B0=9C=EC=84=A0:=20?= =?UTF-8?q?readline=20=EC=9E=85=EB=A0=A5=20=EC=B2=98=EB=A6=AC=20=EC=A4=91?= =?UTF-8?q?=20=EB=B0=9C=EC=83=9D=ED=95=98=EB=8A=94=20=EC=A0=95=EC=88=98=20?= =?UTF-8?q?=EC=98=A4=EB=B2=84=ED=94=8C=EB=A1=9C=EC=9A=B0(integer=20overflo?= =?UTF-8?q?w)=20=EC=B7=A8=EC=95=BD=EC=A0=90=EC=9D=84=20=ED=95=B4=EA=B2=B0?= =?UTF-8?q?=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=97=90=EC=84=9C=EB=8A=94=20'1'=20=EB=98=90?= =?UTF-8?q?=EB=8A=94=20'2'=EB=A7=8C=20=EA=B8=B0=EB=8C=80=ED=95=98=EB=8A=94?= =?UTF-8?q?=20=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=EC=97=90=20`^[0-9]+$`=EB=9D=BC=EB=8A=94=20?= =?UTF-8?q?=EB=8A=90=EC=8A=A8=ED=95=9C=20=EC=A0=95=EA=B7=9C=20=ED=91=9C?= =?UTF-8?q?=ED=98=84=EC=8B=9D=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=96=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4.=20=EC=9D=B4=EB=8A=94=2032=EB=B9=84?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=EC=88=98=20=ED=95=9C=EA=B3=84=EB=A5=BC=20?= =?UTF-8?q?=EC=B4=88=EA=B3=BC=ED=95=98=EB=8A=94=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EC=9D=B4=20=EB=93=A4=EC=96=B4=EC=98=AC=20=EA=B2=BD=EC=9A=B0,?= =?UTF-8?q?=20R=EC=9D=98=20`as.integer()`=20=ED=95=A8=EC=88=98=EC=97=90=20?= =?UTF-8?q?=EC=9D=98=ED=95=B4=20`NA`=EB=A1=9C=20=EA=B0=95=EC=A0=9C=20?= =?UTF-8?q?=EB=B3=80=ED=99=98=EB=90=98=EC=96=B4=20=EC=9D=B4=ED=9B=84=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=98=20=EC=98=A4=EC=9E=91=EB=8F=99?= =?UTF-8?q?=EC=9D=84=20=EC=9C=A0=EB=B0=9C=ED=95=A0=20=EC=88=98=20=EC=9E=88?= =?UTF-8?q?=EC=8A=B5=EB=8B=88=EB=8B=A4.=20=EC=9D=B4=20=EC=B7=A8=EC=95=BD?= =?UTF-8?q?=EC=A0=90=EC=9D=84=20=ED=95=B4=EA=B2=B0=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20=EC=A0=95=EA=B7=9C=20=ED=91=9C=ED=98=84?= =?UTF-8?q?=EC=8B=9D=EC=9D=84=20`^[12]$`=EB=A1=9C=20=EC=97=84=EA=B2=A9?= =?UTF-8?q?=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95=ED=95=98=EC=97=AC,=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=EC=9D=B4=20=EC=A0=95=ED=99=95=ED=9E=88=20'1'?= =?UTF-8?q?=20=EB=98=90=EB=8A=94=20'2'=EC=9D=B8=20=EA=B2=BD=EC=9A=B0?= =?UTF-8?q?=EC=97=90=EB=A7=8C=20=ED=86=B5=EA=B3=BC=EB=90=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=A9=EC=96=B4=20=EB=A1=9C=EC=A7=81=EC=9D=84=20?= =?UTF-8?q?=EA=B0=95=ED=99=94=ED=96=88=EC=8A=B5=EB=8B=88=EB=8B=A4.=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=EB=90=9C=20Sentinel=20=EC=A0=80=EB=84=90=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=EA=B3=BC=20=EC=9C=A0=ED=9A=A8=EC=84=B1=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EB=8B=A8=EC=9C=84=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=8F=84=20=EC=B6=94=EA=B0=80=ED=96=88=EC=8A=B5?= =?UTF-8?q?=EB=8B=88=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .jules/sentinel.md | 4 ++++ R/aFIPC.R | 6 +++--- tests/testthat/test-readline-validation.R | 7 +++++++ 3 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 tests/testthat/test-readline-validation.R diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a8207a48..54035c51 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -2,3 +2,7 @@ **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-25 - Fix integer overflow vulnerability in readline +**Vulnerability:** Weak regex `^[0-9]+$` on interactive `readline` input allows integers exceeding the 32-bit limit to be passed to `as.integer()`, resulting in `NA` coercion and downstream logic failure. +**Learning:** In R, when validating inputs for strict coercion (like `as.integer()`), broad regex patterns are a security vulnerability. Inputs larger than the maximum integer value will be cast to `NA`. +**Prevention:** Always use strictly bounded exact-match regex (e.g., `^[12]$`) for menu selections or exact digit counts for IDs to prevent overflow coercion vulnerabilities. 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..ba91d527 --- /dev/null +++ b/tests/testthat/test-readline-validation.R @@ -0,0 +1,7 @@ +test_that("autoFIPC validates readline inputs properly", { + expect_true(grepl("^[12]$", "1")) + expect_true(grepl("^[12]$", "2")) + expect_false(grepl("^[12]$", "3")) + expect_false(grepl("^[12]$", "99999999999999999999")) + expect_false(grepl("^[12]$", "10")) +}) From eea7cfff34a2a4d658cccbf7f9115571ba273bcc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:12:20 +0900 Subject: [PATCH 2/3] docs(sentinel): keep menu validation finding local --- .jules/sentinel.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index 54035c51..02985d61 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -1,8 +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. -## 2024-07-25 - Fix integer overflow vulnerability in readline -**Vulnerability:** Weak regex `^[0-9]+$` on interactive `readline` input allows integers exceeding the 32-bit limit to be passed to `as.integer()`, resulting in `NA` coercion and downstream logic failure. -**Learning:** In R, when validating inputs for strict coercion (like `as.integer()`), broad regex patterns are a security vulnerability. Inputs larger than the maximum integer value will be cast to `NA`. -**Prevention:** Always use strictly bounded exact-match regex (e.g., `^[12]$`) for menu selections or exact digit counts for IDs to prevent overflow coercion vulnerabilities. +**Prevention:** Always implement explicit runtime type validation for optional boolean parameters. \ No newline at end of file From 4b38d796e411e4e87c5bf8e9d0ea2f1c65a589fe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:12:56 +0900 Subject: [PATCH 3/3] test(input): exercise autoFIPC menu validation boundary --- tests/testthat/test-readline-validation.R | 51 ++++++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/tests/testthat/test-readline-validation.R b/tests/testthat/test-readline-validation.R index ba91d527..7cbea4a1 100644 --- a/tests/testthat/test-readline-validation.R +++ b/tests/testthat/test-readline-validation.R @@ -1,7 +1,46 @@ -test_that("autoFIPC validates readline inputs properly", { - expect_true(grepl("^[12]$", "1")) - expect_true(grepl("^[12]$", "2")) - expect_false(grepl("^[12]$", "3")) - expect_false(grepl("^[12]$", "99999999999999999999")) - expect_false(grepl("^[12]$", "10")) +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) })