diff --git a/NEWS.md b/NEWS.md index f3db335..1552a57 100644 --- a/NEWS.md +++ b/NEWS.md @@ -21,7 +21,7 @@ * Cached API responses are written atomically and corrupt RDS files now fail with a specific cache-clear instruction instead of falling through to network access. * Cached archive and adapter files without provenance receipts now fail closed when a URL, version, `as_of`, or expected digest is requested. Unconstrained internal reuse warns and records a `legacy_unverified` receipt, while `force = TRUE` replaces the legacy bytes without exposing URL credentials. -* Cached archive and adapter readers now copy verified bytes to a private snapshot while holding the cache lock, so a concurrent refresh cannot separate parsed data from its source receipt. Omitted provenance fields remain unconstrained when a verified snapshot is reused. OAuth and cloud credential parameters are matched by explicit normalized names, including authorization `code`, without hiding benign names such as `author` or `monkey`. +* Cached archive readers now acquire matching bytes and receipts and create a verified private snapshot under one cache lock, so a concurrent forced refresh cannot change the archive being parsed. Adapter readers continue to consume private verified snapshots, and omitted provenance fields remain unconstrained when a snapshot is reused. OAuth and cloud credential parameters are matched by explicit normalized names without hiding benign names such as `author`, `monkey`, or `keyboard`. * Clean-install validation now builds a source tarball, installs it into temporary libraries outside the repository, exercises every public export with deterministic offline fixtures, and runs twice in pull-request CI. * O*NET archive and external-adapter downloads now support optional `expected_sha256` and `as_of` verification, write atomic source receipts with URL, commit when inferable, retrieval time, digest, size, and version metadata, and reject changed or mismatched cached sources. * Pull requests now require installed-package tests with network access blocked plus a complete pkgdown reference check and site build; deployment remains limited to pushes on `main`. diff --git a/R/cache.R b/R/cache.R index d5ccb2b..1be850e 100644 --- a/R/cache.R +++ b/R/cache.R @@ -187,12 +187,25 @@ onet_receipt_value <- function(x) { as.character(x) } -onet_url_parameter_is_sensitive <- function(name) { +onet_normalize_url_parameter_name <- function(name) { name <- utils::URLdecode(name) - name <- gsub("([A-Z]+)([A-Z][a-z])", "\\1_\\2", name, perl = TRUE) - name <- gsub("([a-z0-9])([A-Z])", "\\1_\\2", name, perl = TRUE) - name <- tolower(name) - name <- gsub("[-.]", "_", name) + name <- gsub( + "([[:upper:]]+)([[:upper:]][[:lower:]])", + "\\1_\\2", + name, + perl = TRUE + ) + name <- gsub( + "([[:lower:][:digit:]])([[:upper:]])", + "\\1_\\2", + name, + perl = TRUE + ) + tolower(gsub("[.-]+", "_", name)) +} + +onet_url_parameter_is_sensitive <- function(name) { + name <- onet_normalize_url_parameter_name(name) sensitive_names <- c( "auth", "authorization", @@ -203,55 +216,61 @@ onet_url_parameter_is_sensitive <- function(name) { "credential", "credentials", "key", + "access_token", + "id_token", + "refresh_token", + "token", + "api_token", "api_key", "apikey", "x_api_key", "subscription_key", "ocp_apim_subscription_key", - "token", - "api_token", "auth_token", - "access_token", - "id_token", - "refresh_token", "oauth_token", "bearer_token", "security_token", "session_token", + "access_key", + "access_key_id", + "secret_key", + "consumer_key", "client_secret", + "consumer_secret", "client_assertion", "assertion", "jwt", "saml_response", "secret", - "secret_key", "secret_access_key", "private_key", "private_token", "personal_access_token", "password", "passwd", + "pwd", "signature", "sig", "oauth_signature", + "oauth_verifier", "sas", "shared_access_signature", - "access_key", - "access_key_id", - "aws_access_key_id", - "aws_secret_access_key", - "aws_session_token", - "aws_security_token", + "sas_token", "account_key", "storage_account_key", "shared_access_key", "connection_string", + "aws_access_key_id", + "aws_secret_access_key", + "aws_session_token", + "aws_security_token", "x_amz_credential", "x_amz_signature", "x_amz_security_token", "x_goog_credential", "x_goog_signature", - "x_goog_security_token" + "x_goog_security_token", + "google_access_id" ) name %in% sensitive_names || gsub("_", "", name, fixed = TRUE) %in% @@ -419,6 +438,19 @@ onet_atomic_commit_source <- function( dest, receipt, return_snapshot = FALSE) { + onet_with_cache_lock(dest, onet_atomic_commit_source_unlocked( + tmp = tmp, + dest = dest, + receipt = receipt, + return_snapshot = return_snapshot + )) +} + +onet_atomic_commit_source_unlocked <- function( + tmp, + dest, + receipt, + return_snapshot = FALSE) { receipt_dest <- onet_receipt_path(dest) receipt_tmp <- tempfile( paste0(".", basename(receipt_dest), "-write-"), @@ -441,7 +473,7 @@ onet_atomic_commit_source <- function( }, add = TRUE) } - result <- onet_with_cache_lock(dest, { + commit <- function() { old_paths <- c(dest, receipt_dest) backups <- rep(NA_character_, length(old_paths)) installed <- character() @@ -504,7 +536,8 @@ onet_atomic_commit_source <- function( } else { invisible(dest) } - }) + } + result <- commit() snapshot_success <- isTRUE(return_snapshot) result } @@ -587,6 +620,21 @@ onet_cached_source_snapshot <- function( expected_sha256 = NULL, version = NULL, as_of = NULL) { + onet_with_cache_lock(path, onet_cached_source_snapshot_unlocked( + path = path, + source_url = source_url, + expected_sha256 = expected_sha256, + version = version, + as_of = as_of + )) +} + +onet_cached_source_snapshot_unlocked <- function( + path, + source_url = NULL, + expected_sha256 = NULL, + version = NULL, + as_of = NULL) { extension <- tools::file_ext(path) snapshot <- tempfile( "onet-cache-snapshot-", @@ -599,16 +647,14 @@ onet_cached_source_snapshot <- function( } }, add = TRUE) - result <- onet_with_cache_lock(path, { - receipt <- onet_cached_source_receipt_unlocked( - path = path, - source_url = source_url, - expected_sha256 = expected_sha256, - version = version, - as_of = as_of - ) - onet_copy_verified_snapshot(path, receipt, snapshot) - }) + receipt <- onet_cached_source_receipt_unlocked( + path = path, + source_url = source_url, + expected_sha256 = expected_sha256, + version = version, + as_of = as_of + ) + result <- onet_copy_verified_snapshot(path, receipt, snapshot) success <- TRUE result } diff --git a/R/panel.R b/R/panel.R index 0f035d3..6395eef 100644 --- a/R/panel.R +++ b/R/panel.R @@ -106,9 +106,9 @@ archive_version_from_link <- function(link) { #' supply `expected_sha256` when an independently verified digest is available. #' A cached archive without a receipt cannot satisfy the requested URL or #' version provenance. Use `force = TRUE` to replace legacy cached bytes. -#' The returned path names the shared cache entry, which a later `force = TRUE` -#' call may replace. Use [onet_archive_read()] to parse a verified private -#' snapshot rather than reopening the shared path. +#' The returned path is the shared cache location and can be replaced by a later +#' forced download. [onet_archive_read()] parses a private verified snapshot +#' rather than this mutable path. #' @export #' #' @examples @@ -122,7 +122,7 @@ onet_archive_download <- function( force = FALSE, expected_sha256 = NULL, as_of = NULL) { - path <- onet_archive_acquire( + acquired <- onet_archive_acquire( version = version, dir = dir, force = force, @@ -130,7 +130,7 @@ onet_archive_download <- function( as_of = as_of, return_snapshot = FALSE ) - path + acquired } onet_archive_acquire <- function( @@ -145,9 +145,11 @@ onet_archive_acquire <- function( if (!is.logical(force) || length(force) != 1 || is.na(force)) { cli::cli_abort("{.arg force} must be `TRUE` or `FALSE`.") } - if (!is.logical(return_snapshot) || + if ( + !is.logical(return_snapshot) || length(return_snapshot) != 1 || - is.na(return_snapshot)) { + is.na(return_snapshot) + ) { cli::cli_abort("{.arg return_snapshot} must be `TRUE` or `FALSE`.") } expected_sha256 <- onet_normalize_sha256(expected_sha256) @@ -164,36 +166,78 @@ onet_archive_acquire <- function( dest_name <- basename(sub("[?#].*$", "", release$text_url[[1]])) dest <- file.path(archive_dir, dest_name) - if (file.exists(dest) && file.info(dest)$size > 0 && !isTRUE(force)) { - snapshot <- onet_cached_source_snapshot( - path = dest, - source_url = release$text_url[[1]], - expected_sha256 = expected_sha256, - version = version, - as_of = as_of - ) - keep_snapshot <- FALSE - on.exit({ - if (!keep_snapshot) { - unlink(snapshot, force = TRUE) + onet_with_cache_lock( + dest, + { + if (file.exists(dest) && file.info(dest)$size > 0 && !isTRUE(force)) { + if (isTRUE(return_snapshot)) { + snapshot <- onet_cached_source_snapshot_unlocked( + path = dest, + source_url = release$text_url[[1]], + expected_sha256 = expected_sha256, + version = version, + as_of = as_of + ) + snapshot <- onet_validate_archive_snapshot(snapshot) + receipt <- attr(snapshot, "source_receipt", exact = TRUE) + result <- list( + snapshot = snapshot, + receipt = receipt, + cache_path = dest + ) + } else { + onet_cached_source_receipt_unlocked( + path = dest, + source_url = release$text_url[[1]], + expected_sha256 = expected_sha256, + version = version, + as_of = as_of + ) + validate_archive_zip(dest, remove_invalid = FALSE) + result <- dest + } + } else { + tmp <- tempfile("onet-archive-", tmpdir = archive_dir, fileext = ".zip") + on.exit(unlink(tmp, force = TRUE), add = TRUE) + onet_archive_download_file(release$text_url[[1]], tmp) + receipt <- onet_source_receipt( + path = tmp, + source_url = release$text_url[[1]], + expected_sha256 = expected_sha256, + version = version, + as_of = as_of + ) + validate_archive_zip(tmp) + committed <- onet_atomic_commit_source_unlocked( + tmp, + dest, + receipt, + return_snapshot = return_snapshot + ) + if (isTRUE(return_snapshot)) { + snapshot <- onet_validate_archive_snapshot(committed) + result <- list( + snapshot = snapshot, + receipt = receipt, + cache_path = dest + ) + } else { + result <- committed + } } - }, add = TRUE) - validate_archive_zip(snapshot) - if (isTRUE(return_snapshot)) { - keep_snapshot <- TRUE - return(snapshot) - } - return(dest) - } + result + }, + timeout = max(300, getOption("timeout", 60)) + ) +} - tmp <- tempfile("onet-archive-", tmpdir = archive_dir, fileext = ".zip") - on.exit(unlink(tmp, force = TRUE), add = TRUE) +onet_archive_download_file <- function(url, dest) { download_warned <- FALSE status <- tryCatch( withCallingHandlers( utils::download.file( - url = release$text_url[[1]], - destfile = tmp, + url = url, + destfile = dest, mode = "wb", quiet = TRUE ), @@ -203,7 +247,7 @@ onet_archive_acquire <- function( } ), error = function(cnd) { - safe_url <- onet_redact_url_credentials(release$text_url[[1]]) + safe_url <- onet_redact_url_credentials(url) cli::cli_abort( c( "Failed to download O*NET archive.", @@ -213,10 +257,10 @@ onet_archive_acquire <- function( } ) if (download_warned && identical(status, 0L)) { - onet_warn_download_completed(release$text_url[[1]], "O*NET archive") + onet_warn_download_completed(url, "O*NET archive") } if (!identical(status, 0L)) { - safe_url <- onet_redact_url_credentials(release$text_url[[1]]) + safe_url <- onet_redact_url_credentials(url) cli::cli_abort( c( "Failed to download O*NET archive.", @@ -224,21 +268,19 @@ onet_archive_acquire <- function( ) ) } + invisible(dest) +} - receipt <- onet_source_receipt( - path = tmp, - source_url = release$text_url[[1]], - expected_sha256 = expected_sha256, - version = version, - as_of = as_of - ) - validate_archive_zip(tmp) - onet_atomic_commit_source( - tmp, - dest, - receipt, - return_snapshot = return_snapshot - ) +onet_validate_archive_snapshot <- function(snapshot) { + success <- FALSE + on.exit({ + if (!success) { + unlink(snapshot, force = TRUE) + } + }, add = TRUE) + validate_archive_zip(snapshot) + success <- TRUE + snapshot } #' Read an O*NET Archive Table @@ -271,12 +313,9 @@ onet_archive_read <- function(version, table, path = NULL, release_date = NULL) } archive <- if (is.null(path)) { - snapshot <- onet_archive_acquire( - version = version, - return_snapshot = TRUE - ) - on.exit(unlink(snapshot, force = TRUE), add = TRUE) - snapshot + acquired <- onet_archive_acquire(version, return_snapshot = TRUE) + on.exit(unlink(acquired$snapshot, force = TRUE), add = TRUE) + acquired$snapshot } else { path } @@ -771,11 +810,13 @@ onet_archive_member <- function(archive, table) { match[[1]] } -validate_archive_zip <- function(path) { +validate_archive_zip <- function(path, remove_invalid = TRUE) { tryCatch( utils::unzip(path, list = TRUE), error = function(cnd) { - unlink(path, force = TRUE) + if (isTRUE(remove_invalid)) { + unlink(path, force = TRUE) + } cli::cli_abort("Downloaded archive is not a readable ZIP file.", parent = cnd) } ) diff --git a/man/onet_archive_download.Rd b/man/onet_archive_download.Rd index d3198bb..f8fbd12 100644 --- a/man/onet_archive_download.Rd +++ b/man/onet_archive_download.Rd @@ -41,9 +41,9 @@ optional \code{as_of} metadata. The archive URLs are not assumed to be immutable supply \code{expected_sha256} when an independently verified digest is available. A cached archive without a receipt cannot satisfy the requested URL or version provenance. Use \code{force = TRUE} to replace legacy cached bytes. -The returned path names the shared cache entry, which a later \code{force = TRUE} -call may replace. Use \code{\link[=onet_archive_read]{onet_archive_read()}} to parse a verified private -snapshot rather than reopening the shared path. +The returned path is the shared cache location and can be replaced by a later +forced download. \code{\link[=onet_archive_read]{onet_archive_read()}} parses a private verified snapshot +rather than this mutable path. } \examples{ if (interactive()) { diff --git a/tests/testthat/test-panel.R b/tests/testthat/test-panel.R index bfdf57e..e8b8c6c 100644 --- a/tests/testthat/test-panel.R +++ b/tests/testthat/test-panel.R @@ -134,6 +134,7 @@ test_that("archive cache verifies digests and records a receipt", { as_of = "2026-05" ) saveRDS(receipt, paste0(dest, ".receipt.rds")) + original_validate <- onet2r:::validate_archive_zip local_mocked_bindings( onet_releases = function() { @@ -142,6 +143,13 @@ test_that("archive cache verifies digests and records a receipt", { text_url = url ) }, + onet_copy_cache_snapshot = function(...) { + stop("public archive download created a private snapshot") + }, + validate_archive_zip = function(path, remove_invalid = TRUE) { + expect_equal(remove_invalid, FALSE) + original_validate(path, remove_invalid = remove_invalid) + }, .package = "onet2r" ) @@ -305,6 +313,9 @@ test_that("archive force redownload replaces a receiptless legacy archive", { onet_releases = function() { tibble::tibble(version = "30.3", text_url = source_url) }, + onet_copy_cache_snapshot = function(...) { + stop("public archive download created a private snapshot") + }, .package = "onet2r" ) @@ -327,17 +338,70 @@ test_that("archive force redownload replaces a receiptless legacy archive", { expect_equal(receipt$provenance_status, "recorded") }) +test_that("archive force acquisition returns matching private bytes and receipt", { + source <- tiny_archive_zip(first_value = 7.12) + source_url <- paste0( + "file:///", + sub("^/", "", normalizePath(source, winslash = "/")) + ) + cache_dir <- withr::local_tempdir() + archive_dir <- file.path(cache_dir, "archives") + dir.create(archive_dir) + dest <- file.path(archive_dir, basename(source)) + writeBin(charToRaw("legacy bytes"), dest) + original_copy <- onet2r:::onet_copy_cache_snapshot + original_lock <- onet2r:::onet_with_cache_lock + copied_under_lock <- FALSE + lock_timeout <- NULL + + local_mocked_bindings( + onet_releases = function() { + tibble::tibble(version = "30.3", text_url = source_url) + }, + onet_copy_cache_snapshot = function(from, to) { + copied_under_lock <<- dir.exists(paste0(dest, ".lock")) + original_copy(from, to) + }, + onet_with_cache_lock = function(path, code, timeout = 10) { + lock_timeout <<- timeout + original_lock(path, force(code), timeout = timeout) + }, + .package = "onet2r" + ) + + acquired <- onet2r:::onet_archive_acquire( + "30.3", + dir = cache_dir, + force = TRUE, + as_of = "2026-05", + return_snapshot = TRUE + ) + on.exit(unlink(acquired$snapshot, force = TRUE), add = TRUE) + + expect_equal(copied_under_lock, TRUE) + expect_gte(lock_timeout, 300) + expect_equal(acquired$cache_path, dest) + expect_equal( + onet2r:::onet_sha256(acquired$snapshot), + acquired$receipt$actual_sha256 + ) + expect_equal( + attr(acquired$snapshot, "source_receipt", exact = TRUE), + acquired$receipt + ) + expect_equal(acquired$receipt$as_of, "2026-05") +}) + test_that("onet_archive_read normalizes descriptor archive tables", { zipfile <- tiny_archive_zip() url <- "https://www.onetcenter.org/dl_files/database/db_30_3_text.zip" - saveRDS( - onet2r:::onet_source_receipt( - zipfile, - source_url = url, - version = "30.3" - ), - paste0(zipfile, ".receipt.rds") + receipt <- onet2r:::onet_source_receipt( + zipfile, + source_url = url, + version = "30.3" ) + snapshot <- tempfile(fileext = ".zip") + file.copy(zipfile, snapshot) local_mocked_bindings( onet_archive_acquire = function( @@ -349,7 +413,11 @@ test_that("onet_archive_read normalizes descriptor archive tables", { return_snapshot = FALSE) { expect_equal(version, "30.3") expect_equal(return_snapshot, TRUE) - zipfile + list( + snapshot = structure(snapshot, source_receipt = receipt), + receipt = receipt, + cache_path = zipfile + ) }, onet_releases = function() { tibble::tibble( @@ -405,9 +473,11 @@ test_that("archive reads consume a verified snapshot during cache replacement", ) saveRDS(receipt_a, paste0(dest, ".receipt.rds")) original_member <- onet2r:::onet_archive_member + original_copy <- onet2r:::onet_copy_cache_snapshot snapshot_path <- NULL - snapshot_sha256 <- NULL snapshot_receipt <- NULL + snapshot_digest <- NULL + snapshot_copied_under_lock <- FALSE replaced <- FALSE local_mocked_bindings( @@ -423,10 +493,14 @@ test_that("archive reads consume a verified snapshot during cache replacement", text_url = url ) }, + onet_copy_cache_snapshot = function(from, to) { + snapshot_copied_under_lock <<- dir.exists(paste0(dest, ".lock")) + original_copy(from, to) + }, onet_archive_member = function(archive, table) { snapshot_path <<- archive - snapshot_sha256 <<- onet2r:::onet_sha256(archive) snapshot_receipt <<- attr(archive, "source_receipt", exact = TRUE) + snapshot_digest <<- onet2r:::onet_sha256(archive) if (!replaced) { replacement <- tempfile("archive-b-", tmpdir = archive_dir, fileext = ".zip") file.copy(source_b, replacement) @@ -446,58 +520,63 @@ test_that("archive reads consume a verified snapshot during cache replacement", result <- onet_archive_read("30.3", "Abilities") expect_equal(result$data_value[[1]], 4.12) - expect_equal(snapshot_sha256, snapshot_receipt$actual_sha256) + expect_equal(snapshot_copied_under_lock, TRUE) + expect_equal(snapshot_digest, receipt_a$actual_sha256) expect_equal(snapshot_receipt$actual_sha256, receipt_a$actual_sha256) expect_equal(snapshot_receipt$as_of, "2026-05") expect_equal(onet2r:::onet_sha256(dest), onet2r:::onet_sha256(source_b)) + expect_equal( + readRDS(paste0(dest, ".receipt.rds"))$actual_sha256, + onet2r:::onet_sha256(source_b) + ) expect_equal(file.exists(snapshot_path), FALSE) expect_equal(dir.exists(paste0(dest, ".lock")), FALSE) }) -test_that("forced archive acquisition snapshots committed bytes under one lock", { - source <- tiny_archive_zip(first_value = 7.12) - source_url <- paste0( - "file:///", - sub("^/", "", normalizePath(source, winslash = "/")) - ) +test_that("archive snapshots are removed when archive parsing errors", { + source <- tiny_archive_zip() cache_dir <- withr::local_tempdir() archive_dir <- file.path(cache_dir, "archives") dir.create(archive_dir) - dest <- file.path(archive_dir, basename(source)) - writeBin(charToRaw("legacy bytes"), dest) - original_copy <- onet2r:::onet_copy_cache_snapshot - snapshot_copied_under_lock <- FALSE + url <- "https://www.onetcenter.org/dl_files/database/db_30_3_text.zip" + dest <- file.path(archive_dir, basename(url)) + file.copy(source, dest) + saveRDS( + onet2r:::onet_source_receipt( + dest, + source_url = url, + version = "30.3" + ), + paste0(dest, ".receipt.rds") + ) + snapshot_path <- NULL local_mocked_bindings( + onet_cache_dir = function() cache_dir, onet_releases = function() { - tibble::tibble(version = "30.3", text_url = source_url) + tibble::tibble( + version = "30.3", + release_date = as.Date("2026-05-01"), + soc_vintage = "2019", + text_url = url + ) }, - onet_copy_cache_snapshot = function(from, to) { - snapshot_copied_under_lock <<- dir.exists(paste0(dest, ".lock")) - original_copy(from, to) + onet_archive_member = function(archive, table) { + snapshot_path <<- archive + stop("injected archive parse failure") }, .package = "onet2r" ) - snapshot <- onet2r:::onet_archive_acquire( - "30.3", - dir = cache_dir, - force = TRUE, - expected_sha256 = onet2r:::onet_sha256(source), - as_of = "2026-05", - return_snapshot = TRUE + expect_error( + onet_archive_read("30.3", "Abilities"), + "injected archive parse failure" ) - on.exit(unlink(snapshot, force = TRUE), add = TRUE) - receipt <- attr(snapshot, "source_receipt", exact = TRUE) - - expect_equal(snapshot_copied_under_lock, TRUE) - expect_equal(onet2r:::onet_sha256(snapshot), receipt$actual_sha256) - expect_equal(receipt$actual_sha256, onet2r:::onet_sha256(dest)) - expect_equal(receipt$as_of, "2026-05") - expect_equal(attr(snapshot, "cache_path", exact = TRUE), dest) + expect_equal(file.exists(snapshot_path), FALSE) + expect_equal(dir.exists(paste0(dest, ".lock")), FALSE) }) -test_that("archive snapshots are removed when archive parsing errors", { +test_that("failed archive acquisition removes snapshots and cache locks", { source <- tiny_archive_zip() cache_dir <- withr::local_tempdir() archive_dir <- file.path(cache_dir, "archives") @@ -516,25 +595,23 @@ test_that("archive snapshots are removed when archive parsing errors", { snapshot_path <- NULL local_mocked_bindings( - onet_cache_dir = function() cache_dir, onet_releases = function() { - tibble::tibble( - version = "30.3", - release_date = as.Date("2026-05-01"), - soc_vintage = "2019", - text_url = url - ) + tibble::tibble(version = "30.3", text_url = url) }, - onet_archive_member = function(archive, table) { - snapshot_path <<- archive - stop("injected archive parse failure") + validate_archive_zip = function(path) { + snapshot_path <<- path + stop("injected archive validation failure") }, .package = "onet2r" ) expect_error( - onet_archive_read("30.3", "Abilities"), - "injected archive parse failure" + onet2r:::onet_archive_acquire( + "30.3", + dir = cache_dir, + return_snapshot = TRUE + ), + "injected archive validation failure" ) expect_equal(file.exists(snapshot_path), FALSE) expect_equal(dir.exists(paste0(dest, ".lock")), FALSE) diff --git a/tests/testthat/test-request-construction.R b/tests/testthat/test-request-construction.R index 4ab4bd0..0663413 100644 --- a/tests/testthat/test-request-construction.R +++ b/tests/testthat/test-request-construction.R @@ -534,7 +534,9 @@ test_that("credential matching uses explicit normalized parameter names", { "accessToken", "refreshToken", "clientSecret", "oauthSignature", "SecurityToken", "AWSAccessKeyId", "codeVerifier", "clientAssertion", "SecretAccessKey", - "author", "monkey", "hockey", "keyboard", "signature_version" + "accesskey", "secretkey", "consumerkey", "clientsecret", + "author", "monkey", "hockey", "keyboard", "key_board", + "signature_version" ) expect_equal( unname(vapply( @@ -543,8 +545,8 @@ test_that("credential matching uses explicit normalized parameter names", { logical(1) )), c( - rep(TRUE, 23), - rep(FALSE, 5) + rep(TRUE, 27), + rep(FALSE, 6) ) )