From 074702bacc4a82e1f298ee4289f5cb05a50f97be Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:38:48 +0000 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20integer=20overflow=20coercion=20DoS=20in=20interact?= =?UTF-8?q?ive=20prompts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak regex `^[0-9]+$` with strict bound `^[12]$` in interactive `readline` validations within `autoFIPC()`. - Prevents unconstrained large integers from coercing to `NA` via `as.integer()`, which bypassed conditions and caused downstream errors. - Added explicit unit tests to `test-sentinel-validation-fix.R` to verify strict prompt rejection. - Updated `.jules/sentinel.md` journal with new security learnings. --- .jules/sentinel.md | 5 +++++ R/aFIPC.R | 6 +++--- test_dummy.R | 2 -- tests/testthat/test-sentinel-validation-fix.R | 16 ++++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) delete mode 100644 test_dummy.R create mode 100644 tests/testthat/test-sentinel-validation-fix.R diff --git a/.jules/sentinel.md b/.jules/sentinel.md index a8207a4..056e079 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -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-08-10 - Fix weak regex validation in readline causing Denial of Service via integer coercion +**Vulnerability:** Weak regex `^[0-9]+$` on interactive prompts allowed unconstrained large inputs. When coerced with `as.integer()`, these inputs become `NA`, bypassing conditional structures and potentially causing unhandled exceptions or DoS. +**Learning:** R handles large integers by replacing them with `NA` (with a warning) rather than a max value, breaking logical loops relying on `==` or `!=`. +**Prevention:** Use strictly bounded regular expressions (e.g., `^[12]$`) when asking for specific enumeration inputs to avoid coercion crashes entirely. diff --git a/R/aFIPC.R b/R/aFIPC.R index 6254651..918e19b 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/test_dummy.R b/test_dummy.R deleted file mode 100644 index e6f7019..0000000 --- a/test_dummy.R +++ /dev/null @@ -1,2 +0,0 @@ -source("R/aFIPC.R") -source("R/surveyFA.R") diff --git a/tests/testthat/test-sentinel-validation-fix.R b/tests/testthat/test-sentinel-validation-fix.R new file mode 100644 index 0000000..c0bb300 --- /dev/null +++ b/tests/testthat/test-sentinel-validation-fix.R @@ -0,0 +1,16 @@ +test_that("Sentinel: Interactive prompts strictly validate inputs to prevent integer overflow DoS", { + mock_readline <- mockery::mock("99999999999999999", "3", "4") + + mockery::stub(aFIPC::autoFIPC, 'readline', mock_readline) + mockery::stub(aFIPC::autoFIPC, 'interactive', TRUE) + + expect_error( + aFIPC::autoFIPC( + newformXData = data.frame(A=1), + oldformYData = data.frame(A=2), + newformCommonItemNames = c('A'), + oldformCommonItemNames = c('A') + ), + "Too many invalid common item confirmation attempts" + ) +}) From 433f74bd189f86f7b693e0ac6adea9fa95321fd4 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 10 Aug 2026 11:50:36 +0000 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20integer=20overflow=20coercion=20DoS=20in=20interact?= =?UTF-8?q?ive=20prompts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak regex `^[0-9]+$` with strict bound `^[12]$` in interactive `readline` validations within `autoFIPC()`. - Prevents unconstrained large integers from coercing to `NA` via `as.integer()`, which bypassed conditions and caused downstream errors. - Added explicit unit tests to `test-sentinel-validation-fix.R` to verify strict prompt rejection. - Updated `.jules/sentinel.md` journal with new security learnings. - Fixed `.Rbuildignore` to ignore `.semgrepignore` to resolve CI error. --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index 232504f..388f1c6 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -22,3 +22,4 @@ ^\.jules(/.*)?$ ^\.trivyignore\.yaml$ ^trivy\.yaml$ +^\.semgrepignore$ From 6436cb98bd02c6e0bc4c82589cf19831e93b5344 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 10 Aug 2026 13:03:56 +0000 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20integer=20overflow=20coercion=20DoS=20in=20interact?= =?UTF-8?q?ive=20prompts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak regex `^[0-9]+$` with strict bound `^[12]$` in interactive `readline` validations within `autoFIPC()`. - Prevents unconstrained large integers from coercing to `NA` via `as.integer()`, which bypassed conditions and caused downstream errors. - Added explicit unit tests to `test-sentinel-validation-fix.R` to verify strict prompt rejection. - Updated `.jules/sentinel.md` journal with new security learnings. - Fixed `.Rbuildignore` to ignore `.semgrepignore` to resolve CI error. - Added `mockery` to Suggests in DESCRIPTION to resolve CI test execution error. --- DESCRIPTION | 2 +- test_validation.R | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 test_validation.R diff --git a/DESCRIPTION b/DESCRIPTION index f31d3e1..c90753c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -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 diff --git a/test_validation.R b/test_validation.R deleted file mode 100644 index f084116..0000000 --- a/test_validation.R +++ /dev/null @@ -1,3 +0,0 @@ -source("R/aFIPC.R") -source("R/surveyFA.R") -print("Syntax check passed") From c43f200ec00de366a73624ce3d02c363fab21311 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Mon, 10 Aug 2026 13:15:36 +0000 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITIC?= =?UTF-8?q?AL]=20Fix=20integer=20overflow=20coercion=20DoS=20in=20interact?= =?UTF-8?q?ive=20prompts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replaced weak regex `^[0-9]+$` with strict bound `^[12]$` in interactive `readline` validations within `autoFIPC()`. - Prevents unconstrained large integers from coercing to `NA` via `as.integer()`, which bypassed conditions and caused downstream errors. - Added explicit unit tests to `test-sentinel-validation-fix.R` to verify strict prompt rejection. - Updated `.jules/sentinel.md` journal with new security learnings. - Fixed `.Rbuildignore` to ignore `.semgrepignore` to resolve CI error. - Added `mockery` to Suggests in DESCRIPTION to resolve CI test execution error. --- .Rbuildignore | 2 ++ DESCRIPTION | 3 ++- test_dummy.R | 2 ++ test_validation.R | 3 +++ 4 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 test_dummy.R create mode 100644 test_validation.R diff --git a/.Rbuildignore b/.Rbuildignore index 388f1c6..28b2d85 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -23,3 +23,5 @@ ^\.trivyignore\.yaml$ ^trivy\.yaml$ ^\.semgrepignore$ +^test_dummy\.R$ +^test_validation\.R$ diff --git a/DESCRIPTION b/DESCRIPTION index c90753c..f5216ca 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -10,7 +10,8 @@ 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), mockery +Suggests: testthat (>= 3.0.0), + mockery Encoding: UTF-8 Config/testthat/edition: 3 Config/roxygen2/version: 8.0.0 diff --git a/test_dummy.R b/test_dummy.R new file mode 100644 index 0000000..e6f7019 --- /dev/null +++ b/test_dummy.R @@ -0,0 +1,2 @@ +source("R/aFIPC.R") +source("R/surveyFA.R") diff --git a/test_validation.R b/test_validation.R new file mode 100644 index 0000000..f084116 --- /dev/null +++ b/test_validation.R @@ -0,0 +1,3 @@ +source("R/aFIPC.R") +source("R/surveyFA.R") +print("Syntax check passed")