3 Package Development Security Best Practices

This work-in-progress chapter includes guidance about managing secrets in packages and links for further reading.

3.1 Miscellaneous

3.2 Secrets in packages

This section contains guidance for when you develop a package interacting with a web resource requiring credentials (API keys, tokens, etc.). Also refer to the httr vignette about sharing secrets.

3.2.1 Secrets in packages and user protection

Say your package needs an API key for making requests on behalf of users of your package.

  • In your package documentation, guide the user so the API key doesn’t end up in the .Rhistory/script of users of your package.

    • Encourage the use of environment variables to store the API key (or even remove the possibility to pass it as an argument to the functions?). You could link to this intro to startup files and usethis::edit_r_environ().

    • Or your package could depend on, or encourage the use of, keyring to help user store variables in the specific OS’ credential stores (more secure than .Renviron): i.e. you’d create a function for setting the key, and have another one for retrieving the key; or the user would write Sys.setenv(SUPERSECRETKEY = keyring::key_get("myservice")) at the beginning of their script.

    • Do not print the API key even in verbose mode in any message, warning, error.

  • In the GitHub issue template, it should be stated not to share any credentials (it’s the case for rOpenSci issue template). If an user of your package accidentally shares credentials in an issue, make sure they’re aware of that so they can revoke the key (i.e. ask them explicitly in an answer whether they realized they shared their key).

3.2.2 Secrets in packages and development

You’ll need to protect your secrets as you protect secrets of users, but there’s more to take into account and keep in mind.

3.2.2.1 Secrets and recorded requests in tests

If you use vcr or httptest in tests for caching API responses, you need to make sure the recorded requests / fixtures do not contain secrets. Refer to vcr security guidance and httptest guidance “Redacting and Modifying Recorded Requests”, and inspect your recorded requests / fixtures before committing them the first time to be sure you got the setup right.

vcr being an rOpenSci package, you can post any question you might have to rOpenSci forum

3.2.2.2 Share secrets with CI services

Now, you might need to share secrets with continuous integration services.

You could store API keys as environment variables / secrets, referring to the docs of the CI service.

For more details and workflow advice, refer to the gargle article “Managing tokens securely” and the security chapter of the HTTP testing in R book.

Document the steps you made in CONTRIBUTING.md so you, or say a new maintainer, can remember how to do that next time.

3.2.2.3 Secrets and collaborations

What about pull requests from external contributors? Tests using your secrets will fail unless you use some sort of mocked/cached response, so you might want to skip them depending on the context. For instance, in your CI account you could create an environment variable called THIS_IS_ME and then skip tests based on the presence of this variable. This obviously means the PR checks by the CI are not exhaustive, so you’ll need to check out the PR locally to run all tests.

Document the behavior of your package for external PRs in CONTRIBUTING.md for the sake of people making PRs and of people reviewing them (you in a few weeks, and other authors of the package).

3.2.3 Secrets and CRAN

On CRAN, skip any tests (skip_on_cran()) and examples (dontrun) requiring credentials.

Precompute vignettes requiring credentials.

3.3 Further reading

Useful security resources: