From 3341b80df404089fbc96f055b7135ca148b3bc6c Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 7 Aug 2026 04:15:39 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20Fix=20missi?= =?UTF-8?q?ng=20input=20validation=20in=20exported=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added strict input validation for the `nested`, `adj`, and `conf.level` arguments at the beginning of the `vuongtest` and `icci` exported functions. `conf.level` is now checked to be a single numeric value between 0 and 1. `nested` is checked to be a single logical value. `adj` is checked using `match.arg`. This prevents unvalidated arguments from bypassing top-level safeguards and triggering raw R errors deep inside internal logic, which could leak internal execution contexts. --- .jules/sentinel.md | 5 +++++ R/icci.R | 4 ++++ R/vuongtest.R | 7 ++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index bc5c6e1..057690c 100644 --- a/.jules/sentinel.md +++ b/.jules/sentinel.md @@ -12,3 +12,8 @@ **Vulnerability:** Raw `stop()` and `warning()` calls without `call. = FALSE` in `llcont.R` and `vuongtest.R` exposed execution stack/call details when raised. **Learning:** While some instances of `stop()` inside `tryCatch()` were previously fixed to hide the call stack, other standalone exceptions and warnings still leaked call context. Security must be consistently applied across the entire codebase. **Prevention:** Always set `call. = FALSE` when using `stop()` or `warning()` to enforce a secure-by-default boundary and prevent internal execution paths from being disclosed to the end user. + +## 2026-08-07 - Add input validation for exported functions +**Vulnerability:** Unvalidated arguments passed to exported functions (`vuongtest` and `icci`) could bypass top-level `stop(..., call. = FALSE)` safeguards and trigger raw R errors deep inside internal logic, leaking internal execution contexts. +**Learning:** In R codebases, exported functions must explicitly validate argument types, lengths, and bounds (e.g., `length(x) == 1`, `match.arg()`) to fail securely and avoid information disclosure. +**Prevention:** Always strictly validate the type, length, and bounds of user inputs at the very beginning of exported functions, using `stop(..., call. = FALSE)` to securely halt execution on invalid input. diff --git a/R/icci.R b/R/icci.R index f22278a..6a1eb1d 100644 --- a/R/icci.R +++ b/R/icci.R @@ -65,6 +65,10 @@ #' @export icci <- function(object1, object2, conf.level=.95, ll1=llcont, ll2=llcont) { + if (!is.numeric(conf.level) || length(conf.level) != 1 || is.na(conf.level) || conf.level <= 0 || conf.level >= 1) { + stop("Argument 'conf.level' must be a single numeric value between 0 and 1.", call. = FALSE) + } + ## check objects, issue warnings/errors, get classes/calls obinfo <- check.obj(object1, object2) callA <- obinfo$callA; classA <- obinfo$classA diff --git a/R/vuongtest.R b/R/vuongtest.R index 2bdfbf6..69dfbf1 100644 --- a/R/vuongtest.R +++ b/R/vuongtest.R @@ -96,7 +96,12 @@ #' @importMethodsFrom lavaan coef fitted logLik vcov #' @importFrom methods slotNames #' @export -vuongtest <- function(object1, object2, nested=FALSE, adj="none", ll1=llcont, ll2=llcont, score1=NULL, score2=NULL, vc1=vcov, vc2=vcov) { +vuongtest <- function(object1, object2, nested=FALSE, adj=c("none", "aic", "bic"), ll1=llcont, ll2=llcont, score1=NULL, score2=NULL, vc1=vcov, vc2=vcov) { + + if (!is.logical(nested) || length(nested) != 1 || is.na(nested)) { + stop("Argument 'nested' must be a single logical value.", call. = FALSE) + } + adj <- match.arg(adj) ## check objects, issue warnings/errors, get classes/calls obinfo <- check.obj(object1, object2)