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

We recommend the article Ten quick tips for staying safe online by Danielle Smalls and Greg Wilson.

3.2 GitHub access security

  • We recommend you secure your GitHub account with two-factor (authentication) 2FA. It is compulsory for all ropensci GitHub organization members and outside collaborators so make sure to enable it before your package is approved.

  • We also recommend you regularly check who has access to your package repository, and that you prune any unused access (such as from former collaborators).

3.3 https

  • If the web service your package wraps has either https or http, opt for https.

3.4 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.4.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. 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.4.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.4.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.4.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.4.2.3 Secrets and collaborations

What about pull requests from external contributors? On GitHub for instance, secrets are only available for GitHub Actions for pull requests started from the repository itself, not from fork. 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.4.3 Secrets and CRAN

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

Precompute vignettes requiring credentials.

3.5 Further reading

Useful security resources: