From bde9874ed5496e88c05926e968b1f1d677dbab54 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Thu, 13 Aug 2026 03:50:52 +0000 Subject: [PATCH] Add strict input validation to exported functions - Added validation for `conf.level` in `icci` - Added validation for `nested` in `vuongtest` - Ensures inputs fail securely with `call. = FALSE` before causing low-level R errors. --- .jules/sentinel.md | 5 +++++ R/icci.R | 3 +++ R/vuongtest.R | 3 +++ 3 files changed, 11 insertions(+) diff --git a/.jules/sentinel.md b/.jules/sentinel.md index bc5c6e1..03c09fb 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. + +## 2024-05-18 - Input Validation at Exported Boundary +**Vulnerability:** Unvalidated inputs like `conf.level` and `nested` passed to top-level exported functions triggered unhandled base R errors (e.g., `non-numeric argument to binary operator` or `argument is not interpretable as logical`), potentially leaking internal execution context and state. +**Learning:** In R codebases, unvalidated arguments passed to exported functions can bypass top-level safeguards and trigger raw R errors deep inside internal logic. +**Prevention:** Always strictly validate the type, length, and bounds of user inputs at the very beginning of exported functions and fail securely using `stop("...", call. = FALSE)`. diff --git a/R/icci.R b/R/icci.R index f22278a..ae37485 100644 --- a/R/icci.R +++ b/R/icci.R @@ -64,6 +64,9 @@ #' @importFrom stats AIC var qnorm #' @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("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) diff --git a/R/vuongtest.R b/R/vuongtest.R index 2bdfbf6..7a254c9 100644 --- a/R/vuongtest.R +++ b/R/vuongtest.R @@ -97,6 +97,9 @@ #' @importFrom methods slotNames #' @export vuongtest <- function(object1, object2, nested=FALSE, adj="none", ll1=llcont, ll2=llcont, score1=NULL, score2=NULL, vc1=vcov, vc2=vcov) { + if (!is.logical(nested) || length(nested) != 1 || is.na(nested)) { + stop("nested must be a single logical value", call. = FALSE) + } ## check objects, issue warnings/errors, get classes/calls obinfo <- check.obj(object1, object2)