The bad thing about making software is that you can sometimes make it easierfor someone to shoot themselves in the foot. The good thing about softwareis that you can make more software to help them not shoot a foot off.
The R package vcr, an R port of the Ruby library of the same name,records and plays back HTTP requests. Some HTTP requests can have secrets (e.g.,passwords, API keys, etc.) in their requests and/or responses. These secretscan then accidentally end up on the Internet, where bad people may find them.These secrets are sometimes called “truffles”.
There’s a suite of tools out there for finding these truffles (e.g.,truffleHog, gitsecrets) that use tools like regex and entropy.
Despite there being existing tools, users tend to use things that arebuilt in the language(s) they know; that are easy to incorporate into their existing workflows. Towards this end, I’ve been working on a newR package trufflesniffer.
trufflesniffer doesn’t do any fancy entropy stuff, and doesn’t try tofind secrets without any informed knowledge. Rather, the user suppliesthe secrets that they want to look for and trufflesniffer looks forthem. In the future I’d look to see if it can be used withoutany user inputs.
terminology:
- sniff: search for a secret
links:
Install
remotes::install_github("ropenscilabs/trufflesniffer")
library(trufflesniffer)
directory
You can “sniff” a file directory or a package: sniff_one()
# crete a directory
Sys.setenv(A_KEY = "a8d#d%d7g7g4012a4s2")
path <- file.path(tempdir(), "foobar")
dir.create(path)
# no matches
sniff_one(path, Sys.getenv("A_KEY"))
#> named list()
# add files with the secret
cat(paste0("foo\nbar\nhello\nworld\n",
Sys.getenv("A_KEY"), "\n"), file = file.path(path, "stuff.R"))
# matches! prints the line number where the key was found
sniff_one(path, Sys.getenv("A_KEY"))
#> $stuff.R
#> [1] 5
package
sniff through a whole package
foo <- function(key = NULL) {
if (is.null(key)) key <- "mysecretkey"
}
package.skeleton(name = "mypkg", list = "foo", path = tempdir())
pkgpath <- file.path(tempdir(), "mypkg")
list.files(pkgpath, recursive=TRUE)
#> [1] "DESCRIPTION" "man/foo.Rd" "man/mypkg-package.Rd"
#> [4] "NAMESPACE" "R/foo.R" "Read-and-delete-me"
# check the package
sniff_secrets_pkg(dir = pkgpath, secrets = c("mysecretkey"))
#> $mysecretkey
#> $mysecretkey$foo.R
#> [1] 3
fixtures
sniff specifically in a package’s test fixtures.
Create a package
foo <- function(key = NULL) {
if (is.null(key)) key <- "a2s323223asd423adsf4"
}
package.skeleton("herpkg", list = "foo", path = tempdir())
pkgpath <- file.path(tempdir(), "herpkg")
dir.create(file.path(pkgpath, "tests/testthat"), recursive = TRUE)
dir.create(file.path(pkgpath, "tests/fixtures"), recursive = TRUE)
cat("library(vcr)
vcr::vcr_configure('../fixtures',
filter_sensitive_data = list('<<mytoken>>' = Sys.getenv('MY_KEY'))
)\n", file = file.path(pkgpath, "tests/testthat/helper-herpkg.R"))
cat("a2s323223asd423adsf4\n",
file = file.path(pkgpath, "tests/fixtures/foo.yml"))
# check that you have a pkg at herpkg
list.files(pkgpath)
#> [1] "DESCRIPTION" "man" "NAMESPACE"
#> [4] "R" "Read-and-delete-me" "tests"
list.files(file.path(pkgpath, "tests/testthat"))
#> [1] "helper-herpkg.R"
cat(readLines(file.path(pkgpath, "tests/testthat/helper-herpkg.R")),
sep = "\n")
#> library(vcr)
#> vcr::vcr_configure('../fixtures',
#> filter_sensitive_data = list('<<mytoken>>' = Sys.getenv('MY_KEY'))
#> )
list.files(file.path(pkgpath, "tests/fixtures"))
#> [1] "foo.yml"
readLines(file.path(pkgpath, "tests/fixtures/foo.yml"))
#> [1] "a2s323223asd423adsf4"
Check the package
Sys.setenv('MY_KEY' = 'a2s323223asd423adsf4')
sniff_secrets_fixtures(pkgpath)
#> $MY_KEY
#> $MY_KEY$foo.yml
#> [1] 1
sniffer
The function sniffer()
wraps the function sniff_secrets_fixtures()
andpretty prints to optimize non-interactive use. Run from within R or from the command line non-interactively.
Example where a secret is found:
sniffer(pkgpath)
Example where a secret is not found:
Sys.unsetenv('MY_KEY')
sniffer(pkgpath)
To do
There’s more to do. trufflesniffer hasn’t been tested thoroughly yet; I’ll domore testing to make the experience better. In addition, it’d probably be best to integrate this into the R vcr package so that the user doesn’t have totake an extra step to make sure they aren’t going to put any secrets onthe web.
Top comments (0)