Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@
**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-08-23 - Prevent Information Disclosure via Unvalidated Arguments in Exported Functions
**Vulnerability:** Unvalidated arguments (`nested`, `adj` in `vuongtest`, `conf.level` in `icci`) passed to exported functions could trigger raw R errors deep inside internal logic (e.g., `the condition has length > 1`, `missing value where TRUE/FALSE needed`), leaking internal execution contexts.
**Learning:** In R, native control flow functions like `if` are strict and evaluate to raw unhandled errors when given `NA` or vectors of length > 1, bypassing top-level `stop(..., call. = FALSE)` safeguards.
**Prevention:** Always strictly validate the type, length, and bounds of user inputs (e.g., checking for length 1 and `!is.na()`) at the very beginning of exported functions to fail securely with `call. = FALSE`.
4 changes: 4 additions & 0 deletions R/icci.R
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions R/vuongtest.R
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@
#' @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("Argument 'nested' must be a single logical value (TRUE/FALSE)", call. = FALSE)
}
Comment on lines +101 to +103

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Info: nested rejects numeric 0/1 input

is.logical(nested) (vuongtest.R:101) now rejects numeric flags that if(nested) previously accepted. No in-repo caller passes numeric, but external callers relying on numeric would now error.

Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.

if (!is.character(adj) || length(adj) != 1 || is.na(adj) || !(adj %in% c("none", "aic", "bic"))) {
stop("Argument 'adj' must be one of 'none', 'aic', or 'bic'", call. = FALSE)
}
Comment on lines +101 to +106

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Info: Validation order correctly guards is.na

In icci (icci.R:68) and vuongtest (vuongtest.R:101-104) the length check precedes is.na() via || short-circuit, so is.na() only runs on length-1 input. This avoids the vectorized-condition error the fix targets.

Open in Devin Review

Was this helpful? React with πŸ‘ or πŸ‘Ž to provide feedback.


## check objects, issue warnings/errors, get classes/calls
obinfo <- check.obj(object1, object2)
callA <- obinfo$callA; classA <- obinfo$classA
Expand Down
Loading