{"id":31895289,"url":"https://github.com/carlodepieri/vcrpy-encrypt","last_synced_at":"2025-10-13T10:23:51.874Z","repository":{"id":48086168,"uuid":"392770655","full_name":"CarloDePieri/vcrpy-encrypt","owner":"CarloDePieri","description":"Encrypt vcrpy cassettes so they can be safely kept under version control.","archived":false,"fork":false,"pushed_at":"2024-07-14T10:10:25.000Z","size":178,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-31T06:41:38.126Z","etag":null,"topics":["cryptography","python","testing","unit-testing","vcrpy"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/vcrpy-encrypt/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CarloDePieri.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-08-04T17:13:03.000Z","updated_at":"2024-07-14T10:10:28.000Z","dependencies_parsed_at":"2024-07-13T18:09:11.530Z","dependency_job_id":null,"html_url":"https://github.com/CarloDePieri/vcrpy-encrypt","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/CarloDePieri/vcrpy-encrypt","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarloDePieri%2Fvcrpy-encrypt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarloDePieri%2Fvcrpy-encrypt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarloDePieri%2Fvcrpy-encrypt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarloDePieri%2Fvcrpy-encrypt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CarloDePieri","download_url":"https://codeload.github.com/CarloDePieri/vcrpy-encrypt/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CarloDePieri%2Fvcrpy-encrypt/sbom","scorecard":{"id":27405,"data":{"date":"2025-08-11","repo":{"name":"github.com/CarloDePieri/vcrpy-encrypt","commit":"9b6c73cd00e643dac8b8ad3219944fa4c0576573"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/18 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:67: update your workflow using https://app.stepsecurity.io/secureworkflow/CarloDePieri/vcrpy-encrypt/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:69: update your workflow using https://app.stepsecurity.io/secureworkflow/CarloDePieri/vcrpy-encrypt/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/CarloDePieri/vcrpy-encrypt/ci.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:34: update your workflow using https://app.stepsecurity.io/secureworkflow/CarloDePieri/vcrpy-encrypt/ci.yml/main?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:47","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:50","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:83","Warn: pipCommand not pinned by hash: .github/workflows/ci.yml:86","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v3.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 3 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":5,"reason":"5 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-79v4-65xg-pq4g","Warn: Project is vulnerable to: GHSA-h4gh-qq45-vh27","Warn: Project is vulnerable to: GHSA-9hjg-9r4m-mvj7","Warn: Project is vulnerable to: GHSA-pq67-6m6q-mj2v","Warn: Project is vulnerable to: GHSA-48p4-8xcf-vxj5"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-14T18:13:15.006Z","repository_id":48086168,"created_at":"2025-08-14T18:13:15.007Z","updated_at":"2025-08-14T18:13:15.007Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279014684,"owners_count":26085554,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cryptography","python","testing","unit-testing","vcrpy"],"created_at":"2025-10-13T10:23:49.990Z","updated_at":"2025-10-13T10:23:51.868Z","avatar_url":"https://github.com/CarloDePieri.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![PyPI](https://img.shields.io/pypi/v/vcrpy-encrypt)](https://pypi.org/project/vcrpy-encrypt/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/vcrpy-encrypt)](https://pypi.org/project/vcrpy-encrypt/) \n[![ci](https://github.com/CarloDePieri/vcrpy-encrypt/actions/workflows/ci.yml/badge.svg)](https://github.com/CarloDePieri/vcrpy-encrypt/actions/workflows/ci.yml)\n[![Coverage Status](https://coveralls.io/repos/github/CarloDePieri/vcrpy-encrypt/badge.svg?branch=main)](https://coveralls.io/github/CarloDePieri/vcrpy-encrypt?branch=main)\n[![Maintenance](https://img.shields.io/maintenance/yes/2024)](https://github.com/CarloDePieri/vcrpy-encrypt/)\n\nEncrypt vcrpy cassettes, so they can be safely kept under version control.\n\n## Rationale\n\nSensitive data can easily end up in HTTP requests/responses during testing.\n[Vcrpy](https://vcrpy.readthedocs.io/en/latest/index.html) has\n[a way to scrub that data](https://vcrpy.readthedocs.io/en/latest/advanced.html#filter-sensitive-data-from-the-request)\nfrom its cassettes, but sometimes the tests that logged them actually need that information to pass.\n\nThis would normally result in a choice: either don't record those test cassettes or don't keep them under version\ncontrol so that they can remain local only.\n\nEnters vcrpy-encrypt: at its core it's a simple\n[vcrpy persister](https://vcrpy.readthedocs.io/en/latest/advanced.html#register-your-own-cassette-persister) that will\nencrypt cassettes before writing them to disk and decrypt them before replaying them when needed by a test. This means\nthat tests can replay cassettes with sensitive data in them AND those cassettes can now be safely kept under version\ncontrol and shared with others.\n\n## Install\n\nSimply run:\n\n```bash\npip install vcrpy-encrypt\n```\n\n## Usage\n\n### Provide a secret key\n\nA secret key is needed to encrypt cassettes. It must be a 128, a 192 or a 256 bits long `bytes` object. vcrpy-encrypt\noffers an easy way to generate a random key:\n\n```bash\npython -c \"from vcrpy_encrypt import generate_key; print(generate_key())\"\n```\n\nBy default this will result in a 128 bits key, but `generate_key` can take `128` or `192` or `256` as argument to\ngenerate a longer key.\n\nIf a specific key is preferred, it can be converted from an utf-8, 16/24/32 characters long string like this:\n\n```python\nkey = \"sixteensecretkey\".encode(\"UTF-8\")  # len(b'sixteensecretkey') == 16\n```\n\nNo matter the source, the key must be kept secret and separated from the code under version control!\n\n### Register the persister\n\nVcrpy's custom persister api needs a class reference. This class can be defined inheriting from\n`BaseEncryptedPersister` like this:\n\n```python\nfrom vcrpy_encrypt import BaseEncryptedPersister\n\nkey = ...  # recover the secret key from somewhere safe\n\nclass MyEncryptedPersister(BaseEncryptedPersister):\n    # the encryption_key field must be initialized here with the chosen key\n    encryption_key: bytes = key\n```\n\nThe encrypted persister can now be registered and used:\n\n```python\nimport vcr\nimport requests\n\n# create a custom vcr object\nmy_vcr = vcr.VCR()\n\n# register the encrypted persister into the custom vcr object\nmy_vcr.register_persister(MyEncryptedPersister)\n\n# use that custom vcr object with use_cassette\nwith my_vcr.use_cassette(\"encoded-cassette\"):\n    # this request will produce an encrypted cassette and will replay it on following runs\n    requests.get(\"https://localhost/?key=super-secret\")\n```\n\nKeep in mind that multiple vcr objects can coexists, so it's possible to use the default vcr everywhere while\nreserving the one with the encrypted persister only for requests resulting in cassettes with sensitive data.\n\n## Customization\n\nSometimes it can be handy to inspect the content of a cassette. This can be done even when using encrypted cassettes:\n\n```python\nclass MyEncryptedPersister(BaseEncryptedPersister):\n    encryption_key: bytes = key\n    should_output_clear_text_as_well = True\n```\n\nThis persister will output a clear text cassette side by side with the encrypted one. Remember to blacklist all clear\ntext cassette in the version control system! For example this will cause git to ignore all `.yaml` file inside a\ncassettes' folder (at any depth):\n\n```bash\n# file: .gitignore\n**/cassettes/**/*.yaml\n```\nClear text cassettes are only useful for human inspection: the persister will still use only the encrypted ones to\nreplay network requests.\n\nIf different cassettes file name suffix are desired, they can be customized:\n\n```python\nclass MyEncryptedPersister(BaseEncryptedPersister):\n    encryption_key: bytes = key\n    should_output_clear_text_as_well = True\n    clear_text_suffix = \".custom_clear\"\n    encoded_suffix = \".custom_enc\"\n```\n\n## Encryption performance\n\nCurrently this library is encrypting cassettes using [cryptography](https://cryptography.io/) with [AES-GCM](https://cryptography.io/en/latest/hazmat/primitives/aead/#cryptography.hazmat.primitives.ciphers.aead.AESGCM). This algorithm\nimplementation is striking a good balance between security and performance.\n\nKeep in mind that key length will have an impact on encrypt time: 128 bits keys should suffice for most use cases.\n\n## Development\n\nInstall [invoke](http://pyinvoke.org/) and [poetry](https://python-poetry.org/):\n\n```bash\npip install invoke poetry\n```\n\nNow clone the git repo:\n\n```bash\ngit clone git@github.com:CarloDePieri/vcrpy-encrypt.git\ncd vcrpy-encrypt\ninv install\n```\n\nThis will try to create a virtualenv based on `python3.8` and install there all\nproject's dependencies. If a different python version is preferred, it can be\nselected by specifying  the `--python` (`-p`) flag like this:\n\n```bash\ninv install -p python3.9\n```\n\nThe test suite can be run with commands:\n\n```bash\ninv test         # run the test suite\ninv test-spec    # run the tests while showing the output as a spec document\ninv test-cov     # run the tests suite and produce a coverage report\n```\n\nTo run the test suite against all supported python version (the executables must be in path!) run:\n\n```bash\ninv test-all-python-versions\n```\n\nTo test the GitHub workflow with [act](https://github.com/nektos/act):\n\n```bash\n# First you need a .secrets file - do not version control this!\necho \"repo_token: your_coveralls_token\" \u003e .secrets\n\n# Then you can run one of these:\ninv act                 # test the workflow\ninv act -s legacy       # open a shell in the legacy-ci act container (the above must fail first!) \ninv act -s new          # open a shell in the ci act container (the above must fail first!) \ninv act -c legacy       # stop and delete a failed legacy-ci act container\ninv act -c new          # stop and delete a failed ci act container\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlodepieri%2Fvcrpy-encrypt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarlodepieri%2Fvcrpy-encrypt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarlodepieri%2Fvcrpy-encrypt/lists"}