From 8898718d5106cc32a54727c3cc9316a2bea448ac Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Tue, 23 Jun 2026 03:53:28 +0000 Subject: [PATCH] Add input validation to exported statistical functions - Added validation for `adj` and `nested` in `vuongtest()` - Added validation for `conf.level` in `icci()` - Prevents invalid arguments from causing unexpected downstream errors --- .jules/sentinel.md | 4 ++++ R/icci.R | 3 +++ R/vuongtest.R | 4 ++++ 3 files changed, 11 insertions(+) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..c630cef --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2026-03-31 - Missing Input Validation on Exported Statistical Functions +**Vulnerability:** Core exported functions like `vuongtest` and `icci` lacked proper parameter validation (e.g., `adj` not constrained to specific strings, `nested` not strictly logical, `conf.level` not checked for valid probability bounds). This could lead to unexpected code execution paths, cryptic R errors downstream, or invalid statistical output when users supply bad input. +**Learning:** In R packages, exported functions that accept string arguments for configuration (`adj`) or numeric parameters with bounds (`conf.level`) must explicitly validate them early using `match.arg()`, `is.numeric()`, `is.logical()` and bounds checking, rather than implicitly trusting the input. +**Prevention:** Consistently apply `match.arg()` for multiple-choice string parameters and explicit `if`/`stop` blocks or `stopifnot()` for type/bounds checking on exported function entry points. \ No newline at end of file diff --git a/R/icci.R b/R/icci.R index f22278a..cd9e9ba 100644 --- a/R/icci.R +++ b/R/icci.R @@ -65,6 +65,9 @@ #' @export icci <- function(object1, object2, conf.level=.95, ll1=llcont, ll2=llcont) { + ## Input validation + if(!is.numeric(conf.level) || length(conf.level) != 1 || conf.level <= 0 || conf.level >= 1) stop("conf.level must be a numeric value strictly between 0 and 1") + ## 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 57c5f6b..f8745e2 100644 --- a/R/vuongtest.R +++ b/R/vuongtest.R @@ -98,6 +98,10 @@ #' @export vuongtest <- function(object1, object2, nested=FALSE, adj="none", ll1=llcont, ll2=llcont, score1=NULL, score2=NULL, vc1=vcov, vc2=vcov) { + ## Input validation + adj <- match.arg(adj, c("none", "aic", "bic")) + if(!is.logical(nested) || length(nested) != 1) stop("nested must be a logical value of length 1") + ## check objects, issue warnings/errors, get classes/calls obinfo <- check.obj(object1, object2) callA <- obinfo$callA; classA <- obinfo$classA