From b733d79dd727da47c85c07511834e6146ca88a24 Mon Sep 17 00:00:00 2001 From: AWS Date: Fri, 21 Jan 2022 00:32:46 +0000 Subject: [PATCH] Release: 1.1.0 --- .gitignore | 2 +- CONTRIBUTING.md | 2 +- LICENSE | 2 +- README.md | 2 +- VERSION | 2 +- examples/bitbucket+tf_enterprise/main.tf | 2 +- examples/codecommit+tf_oss/main.tf | 2 +- examples/github+tf_oss/main.tf | 2 +- examples/githubenterprise+tf_cloud/main.tf | 2 +- git-secrets/.gitattributes | 10 + git-secrets/.github/PULL_REQUEST_TEMPLATE.md | 6 + git-secrets/.pre-commit-hooks.yaml | 5 + git-secrets/.travis.yml | 8 + git-secrets/CHANGELOG.md | 49 + git-secrets/CODE_OF_CONDUCT.md | 4 + git-secrets/CONTRIBUTING.md | 61 ++ git-secrets/LICENSE.txt | 208 +++++ git-secrets/Makefile | 25 + git-secrets/NOTICE.txt | 6 + git-secrets/README.rst | 543 +++++++++++ git-secrets/git-secrets | 357 ++++++++ git-secrets/git-secrets.1 | 843 ++++++++++++++++++ git-secrets/install.ps1 | 48 + git-secrets/test/bats/LICENSE | 20 + git-secrets/test/bats/bin/bats | 1 + git-secrets/test/bats/libexec/bats | 142 +++ git-secrets/test/bats/libexec/bats-exec-suite | 55 ++ git-secrets/test/bats/libexec/bats-exec-test | 346 +++++++ .../test/bats/libexec/bats-format-tap-stream | 165 ++++ git-secrets/test/bats/libexec/bats-preprocess | 52 ++ git-secrets/test/commit-msg.bats | 18 + git-secrets/test/git-secrets.bats | 361 ++++++++ git-secrets/test/pre-commit.bats | 62 ++ git-secrets/test/prepare-commit-msg.bats | 33 + git-secrets/test/test_helper.bash | 94 ++ locals.tf | 2 +- main.tf | 2 +- .../data.tf | 2 +- .../aft-account-provisioning-framework/iam.tf | 2 +- .../iam/role-policies/iam-aft-states.tpl | 2 +- .../iam/trust-policies/lambda.tpl | 2 +- .../iam/trust-policies/states.tpl | 2 +- .../lambda.tf | 32 +- ...isioning_framework_account_metadata_ssm.py | 137 +++ ...ount_provisioning_framework_create_role.py | 30 +- .../iam/trust-policies/aftmanagement.tpl | 2 +- .../schema/request_schema.json | 2 +- .../locals.tf | 2 +- .../states.tf | 3 +- ...ft_account_provisioning_framework.asl.json | 24 +- .../variables.tf | 2 +- .../versions.tf | 2 +- .../aft-account-request-framework/backup.tf | 2 +- modules/aft-account-request-framework/data.tf | 2 +- .../eventbridge.tf | 2 +- modules/aft-account-request-framework/iam.tf | 2 +- .../aft-code-commit-power-user.tpl | 2 +- .../iam/role-policies/aft-get-ct-role.tpl | 2 +- .../iam/role-policies/aft-states.tpl | 2 +- .../iam/role-policies/aft-sts-assume.tpl | 2 +- .../role-policies/aft-sts-assume_ct_admin.tpl | 2 +- .../aftService-gitlab-permissions.tpl | 2 +- .../iam/role-policies/aftService-ssm.tpl | 2 +- .../events-control-tower-events.tpl | 2 +- .../iam/trust-policies/backup.tpl | 2 +- .../iam/trust-policies/events.tpl | 2 +- .../iam/trust-policies/lambda.tpl | 2 +- .../iam/trust-policies/states.tpl | 2 +- modules/aft-account-request-framework/kms.tf | 2 +- .../aft-account-request-framework/lambda.tf | 2 +- .../aft-account-request-framework/locals.tf | 2 +- .../aft-account-request-framework/outputs.tf | 2 +- modules/aft-account-request-framework/sns.tf | 2 +- modules/aft-account-request-framework/sqs.tf | 2 +- .../variables.tf | 2 +- .../aft-account-request-framework/versions.tf | 2 +- modules/aft-account-request-framework/vpc.tf | 1 - modules/aft-code-repositories/codebuild.tf | 2 +- modules/aft-code-repositories/codecommit.tf | 2 +- modules/aft-code-repositories/codepipeline.tf | 1 - modules/aft-code-repositories/codestar.tf | 1 - modules/aft-code-repositories/data.tf | 1 - modules/aft-code-repositories/iam.tf | 2 +- .../ct_aft_codebuild_oss_backend_policy.tpl | 2 +- .../role-policies/ct_aft_codebuild_policy.tpl | 2 +- .../iam/role-policies/ct_aft_cwe_policy.tpl | 2 +- .../iam/trust-policies/codebuild.tpl | 2 +- .../iam/trust-policies/codepipeline.tpl | 2 +- .../iam/trust-policies/events.tpl | 2 +- modules/aft-code-repositories/locals.tf | 2 +- modules/aft-code-repositories/outputs.tf | 2 +- modules/aft-code-repositories/versions.tf | 2 +- modules/aft-customizations/data.tf | 1 - .../aft_codebuild_customizations_policy.tpl | 2 +- ...omizations_invoke_account_provisioning.tpl | 2 +- .../aft_execute_pipeline_lambda.tpl | 2 +- .../aft_get_pipeline_status_lambda.tpl | 2 +- ...ft_states_invoke_customizations_policy.tpl | 2 +- .../ct_aft_codebuild_oss_backend_policy.tpl | 2 +- .../iam/trust-policies/codebuild.tpl | 2 +- .../iam/trust-policies/codepipeline.tpl | 2 +- .../iam/trust-policies/lambda.tpl | 2 +- .../iam/trust-policies/states.tpl | 2 +- .../schema/example_event.json | 2 +- modules/aft-customizations/locals.tf | 2 +- modules/aft-customizations/outputs.tf | 2 +- modules/aft-customizations/pytest.ini | 2 +- modules/aft-customizations/s3.tf | 2 +- modules/aft-customizations/states.tf | 1 - .../states/invoke_customizations.asl.json | 2 +- .../tests/testdata/expected_resources.json | 2 +- .../expected_resources_by_module.json | 2 +- .../tests/testdata/terraform/main.tf | 2 +- .../tests/testdata/terraform/providers.tf | 2 +- modules/aft-customizations/variables.tf | 13 +- modules/aft-customizations/versions.tf | 2 +- modules/aft-feature-options/data.tf | 2 +- modules/aft-feature-options/iam.tf | 2 +- .../aft_delete_default_vpc_lambda.tpl | 2 +- .../role-policies/aft_enable_cloudtrail.tpl | 2 +- .../iam/role-policies/aft_enroll_support.tpl | 2 +- .../iam/role-policies/aft_features_states.tpl | 2 +- .../iam/trust-policies/lambda.tpl | 2 +- .../iam/trust-policies/states.tpl | 2 +- modules/aft-feature-options/kms.tf | 2 +- .../kms/key-policies/log-key.tpl | 2 +- modules/aft-feature-options/locals.tf | 2 +- modules/aft-feature-options/outputs.tf | 2 +- .../s3/bucket-policies/aft_logging_bucket.tpl | 2 +- modules/aft-feature-options/states.tf | 2 +- .../states/aft_features.asl.json | 2 +- modules/aft-feature-options/variables.tf | 2 +- modules/aft-feature-options/versions.tf | 2 +- modules/aft-iam-roles/admin-role/iam.tf | 2 +- modules/aft-iam-roles/admin-role/main.tf | 2 +- .../aft-iam-roles/admin-role/trust_policy.tpl | 2 +- .../iam/aft_admin_role_policy.tpl | 2 +- .../iam/aft_admin_role_trust_policy.tpl | 2 +- modules/aft-iam-roles/outputs.tf | 2 +- modules/aft-iam-roles/versions.tf | 2 +- .../buildspecs/aft-lambda-layer.yml | 5 +- modules/aft-lambda-layer/cloudwatch.tf | 2 +- modules/aft-lambda-layer/codebuild.tf | 2 +- modules/aft-lambda-layer/iam.tf | 2 +- .../iam/trust-policies/codebuild.tpl | 2 +- modules/aft-lambda-layer/locals.tf | 2 +- modules/aft-lambda-layer/outputs.tf | 2 +- modules/aft-lambda-layer/readme.md | 2 +- modules/aft-lambda-layer/variables.tf | 2 +- modules/aft-lambda-layer/versions.tf | 2 +- modules/aft-ssm-parameters/ssm.tf | 2 +- modules/aft-ssm-parameters/variables.tf | 2 +- modules/aft-ssm-parameters/versions.tf | 2 +- outputs.tf | 2 +- providers.tf | 2 +- .../aft-providers.jinja | 2 +- .../customizations_pipeline/backend.jinja | 2 +- .../templates/customizations_pipeline/data.tf | 2 +- .../customizations_pipeline/versions.tf | 2 +- .../api_helpers/post-api-helpers.sh | 2 +- .../api_helpers/pre-api-helpers.sh | 2 +- .../ACCOUNT_TEMPLATE/terraform/backend.jinja | 2 +- .../README.md | 1 - .../terraform/backend.jinja | 2 +- .../terraform/iam.tf | 2 +- .../iam/role-policies/iam-aft-states.tpl | 2 +- .../terraform/iam/trust-policies/states.tpl | 2 +- .../terraform/states.tf | 2 +- .../terraform/states/customizations.asl.json | 2 +- .../terraform/versions.tf | 2 +- .../aft-account-request/README.md | 4 +- .../terraform/backend.jinja | 2 +- .../modules/aft-account-request/ddb.tf | 2 +- .../modules/aft-account-request/variables.tf | 1 - .../modules/aft-account-request/versions.tf | 2 +- .../aft-global-customizations/README.md | 2 +- .../api_helpers/post-api-helpers.sh | 2 +- .../api_helpers/pre-api-helpers.sh | 2 +- .../terraform/backend.jinja | 2 +- .../aft-lambda-layer/aft_common/aft_utils.py | 44 +- sources/aft-lambda-layer/readme.md | 2 +- sources/aft-lambda-layer/setup.py | 2 +- sources/scripts/creds.sh | 2 +- sources/scripts/terraform_client.py | 8 +- versions.tf | 2 +- 185 files changed, 3926 insertions(+), 186 deletions(-) create mode 100644 git-secrets/.gitattributes create mode 100644 git-secrets/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 git-secrets/.pre-commit-hooks.yaml create mode 100644 git-secrets/.travis.yml create mode 100644 git-secrets/CHANGELOG.md create mode 100644 git-secrets/CODE_OF_CONDUCT.md create mode 100644 git-secrets/CONTRIBUTING.md create mode 100644 git-secrets/LICENSE.txt create mode 100644 git-secrets/Makefile create mode 100644 git-secrets/NOTICE.txt create mode 100644 git-secrets/README.rst create mode 100755 git-secrets/git-secrets create mode 100644 git-secrets/git-secrets.1 create mode 100644 git-secrets/install.ps1 create mode 100644 git-secrets/test/bats/LICENSE create mode 120000 git-secrets/test/bats/bin/bats create mode 100755 git-secrets/test/bats/libexec/bats create mode 100755 git-secrets/test/bats/libexec/bats-exec-suite create mode 100755 git-secrets/test/bats/libexec/bats-exec-test create mode 100755 git-secrets/test/bats/libexec/bats-format-tap-stream create mode 100755 git-secrets/test/bats/libexec/bats-preprocess create mode 100644 git-secrets/test/commit-msg.bats create mode 100644 git-secrets/test/git-secrets.bats create mode 100644 git-secrets/test/pre-commit.bats create mode 100644 git-secrets/test/prepare-commit-msg.bats create mode 100644 git-secrets/test/test_helper.bash create mode 100644 modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-account-metadata-ssm/aft_account_provisioning_framework_account_metadata_ssm.py diff --git a/.gitignore b/.gitignore index eea651d5..8dde9ae5 100644 --- a/.gitignore +++ b/.gitignore @@ -131,7 +131,7 @@ mypy-report.xml # pyenv # For a library or package, you might want to ignore these files since the code is # intended to run in multiple environments; otherwise, check them in: -# .python-version +.python-version # pipenv # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d65f8e9..5010f002 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Thank you for your interest in contributing to the AWS Control Tower Account Factory for Terraform. -At this time, we are not accepting contributions. If contributions are accepted in the future, the AWS Control Tower Account Factory for Terraform is released under the [Apache license](http://aws.amazon.com/apache2.0/) and any code submitted will be released under that license. \ No newline at end of file +At this time, we are not accepting contributions. If contributions are accepted in the future, the AWS Control Tower Account Factory for Terraform is released under the [Apache license](http://aws.amazon.com/apache2.0/) and any code submitted will be released under that license. diff --git a/LICENSE b/LICENSE index f49a4e16..261eeb9e 100644 --- a/LICENSE +++ b/LICENSE @@ -198,4 +198,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index 34f96349..5257ddca 100644 --- a/README.md +++ b/README.md @@ -163,4 +163,4 @@ Now that you have configured and deployed AWS Control Tower Account Factory for | [terraform\_version](#output\_terraform\_version) | n/a | | [tf\_backend\_secondary\_region](#output\_tf\_backend\_secondary\_region) | n/a | | [vcs\_provider](#output\_vcs\_provider) | n/a | - \ No newline at end of file + diff --git a/VERSION b/VERSION index bb83058e..9084fa2f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.12 +1.1.0 diff --git a/examples/bitbucket+tf_enterprise/main.tf b/examples/bitbucket+tf_enterprise/main.tf index d9ac2713..6470dbab 100644 --- a/examples/bitbucket+tf_enterprise/main.tf +++ b/examples/bitbucket+tf_enterprise/main.tf @@ -18,4 +18,4 @@ module "aft" { terraform_api_endpoint = "https://terraform.example.com/api/v2/" terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" terraform_org_name = "ExampleOrg" -} \ No newline at end of file +} diff --git a/examples/codecommit+tf_oss/main.tf b/examples/codecommit+tf_oss/main.tf index 2cd86a99..2425d107 100644 --- a/examples/codecommit+tf_oss/main.tf +++ b/examples/codecommit+tf_oss/main.tf @@ -7,4 +7,4 @@ module "aft" { aft_management_account_id = "777788889999" ct_home_region = "us-east-1" tf_backend_secondary_region = "us-west-2" -} \ No newline at end of file +} diff --git a/examples/github+tf_oss/main.tf b/examples/github+tf_oss/main.tf index a990c1c7..d449a839 100644 --- a/examples/github+tf_oss/main.tf +++ b/examples/github+tf_oss/main.tf @@ -13,4 +13,4 @@ module "aft" { global_customizations_repo_name = "ExampleOrg/example-repo-2" account_customizations_repo_name = "ExampleOrg/example-repo-3" account_provisioning_customizations_repo_name = "ExampleOrg/example-repo-4" -} \ No newline at end of file +} diff --git a/examples/githubenterprise+tf_cloud/main.tf b/examples/githubenterprise+tf_cloud/main.tf index d1423bb7..3c032959 100644 --- a/examples/githubenterprise+tf_cloud/main.tf +++ b/examples/githubenterprise+tf_cloud/main.tf @@ -18,4 +18,4 @@ module "aft" { terraform_distribution = "tfc" terraform_token = "EXAMPLE-uoc1c1qsw7poexampleewjeno1pte3rw" terraform_org_name = "ExampleOrg" -} \ No newline at end of file +} diff --git a/git-secrets/.gitattributes b/git-secrets/.gitattributes new file mode 100644 index 00000000..107aad84 --- /dev/null +++ b/git-secrets/.gitattributes @@ -0,0 +1,10 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto + +# Force the bash scripts to be checked out with LF line endings. +git-secrets text eol=lf +git-secrets.1 text eol=lf +test/bats/bin/* text eol=lf +test/bats/libexec/* text eol=lf +*.bats text eol=lf +*.bash text eol=lf \ No newline at end of file diff --git a/git-secrets/.github/PULL_REQUEST_TEMPLATE.md b/git-secrets/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..ab40d21d --- /dev/null +++ b/git-secrets/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,6 @@ +*Issue #, if available:* + +*Description of changes:* + + +By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. diff --git a/git-secrets/.pre-commit-hooks.yaml b/git-secrets/.pre-commit-hooks.yaml new file mode 100644 index 00000000..d3138362 --- /dev/null +++ b/git-secrets/.pre-commit-hooks.yaml @@ -0,0 +1,5 @@ +- id: git-secrets + name: Git Secrets + description: git-secrets scans commits, commit messages, and --no-ff merges to prevent adding secrets into your git repositories. + entry: 'git-secrets --pre_commit_hook' + language: script diff --git a/git-secrets/.travis.yml b/git-secrets/.travis.yml new file mode 100644 index 00000000..25937989 --- /dev/null +++ b/git-secrets/.travis.yml @@ -0,0 +1,8 @@ +language: bash + +before_install: +- git config --global user.email "you@example.com" +- git config --global user.name "Your Name" + +script: +- make test diff --git a/git-secrets/CHANGELOG.md b/git-secrets/CHANGELOG.md new file mode 100644 index 00000000..cfcae4e8 --- /dev/null +++ b/git-secrets/CHANGELOG.md @@ -0,0 +1,49 @@ +# CHANGELOG + +## 1.3.0 - 2019-02-10 + +* Empty provider output is now excluded + (https://github.com/awslabs/git-secrets/issues/34) +* Spaces are now supported in git exec path, making more Windows + paths execute properly. +* Patterns with newlines and carriage returns are now loaded properly. +* Patterns that contain only "\n" are now ignored. +* Various Bash 4 fixes (https://github.com/awslabs/git-secrets/issues/66). +* Make IAM key scanning much more targeted. + +## 1.2.1 - 2016-06-27 + +* Fixed an issue where secret provider commands were causing "command not + found" errors due to a previously set IFS variable. + https://github.com/awslabs/git-secrets/pull/30 + +## 1.2.0 - 2016-05-23 + +* Fixed an issue where spaces files with spaces in their names were not being + properly scanned in the pre-commit hook. +* Now ignoring empty lines and comments (e.g., `#`) in the .gitallowed file. +* Fixed an issue where numbers were being compared to strings causing failures + on some platforms. + +## 1.1.0 - 2016-04-06 + +* Bug fix: the pre-commit hook previously only scanned the working directory + rather than staged files. This release updates the pre-commit hook to instead + scan staged files so that git-secrets will detect violations if the working + directory drifts from the staging directory. +* Added the `--scan-history` subcommand so that you can scan your entire + git history for violations. +* Added the ability to filter false positives by using a .gitallowed file. +* Added support for `--cached`, `--no-index`, and `--untracked` to the `--scan` + subcommand. + +## 1.0.1 - 2016-01-11 + +* Now works correctly with filenames in a repository that contain spaces when + executing `git secrets --scan` with no provided filename (via `git grep`). +* Now works with git repositories with hundreds of thousands of files when + using `git secrets --scan` with no provided filename (via `git grep`). + +## 1.0.0 - 2015-12-10 + +* Initial release of ``git-secrets``. diff --git a/git-secrets/CODE_OF_CONDUCT.md b/git-secrets/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..3b644668 --- /dev/null +++ b/git-secrets/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/git-secrets/CONTRIBUTING.md b/git-secrets/CONTRIBUTING.md new file mode 100644 index 00000000..de6d3d38 --- /dev/null +++ b/git-secrets/CONTRIBUTING.md @@ -0,0 +1,61 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check [existing open](https://github.com/awslabs/git-secrets/issues), or [recently closed](https://github.com/awslabs/git-secrets/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *master* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/git-secrets/labels/help%20wanted) issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](https://github.com/awslabs/git-secrets/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/git-secrets/LICENSE.txt b/git-secrets/LICENSE.txt new file mode 100644 index 00000000..de96b947 --- /dev/null +++ b/git-secrets/LICENSE.txt @@ -0,0 +1,208 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +Note: Other license terms may apply to certain, identified software files +contained within or distributed with the accompanying software if such terms +are included in the directory containing the accompanying software. Such other +license terms will then apply in lieu of the terms of the software license +above. diff --git a/git-secrets/Makefile b/git-secrets/Makefile new file mode 100644 index 00000000..a67eee2d --- /dev/null +++ b/git-secrets/Makefile @@ -0,0 +1,25 @@ +PREFIX ?= /usr/local +MANPREFIX ?= "${PREFIX}/share/man/man1" + +help: + @echo "Please use \`make ' where is one of" + @echo " test to perform unit tests." + @echo " man to build the man file from README.rst" + @echo " install to install. Use PREFIX and MANPREFIX to customize." + +# We use bats for testing: https://github.com/sstephenson/bats +test: + LANG=C test/bats/bin/bats test/ + +# The man page is completely derived from README.rst. Edits to +# README.rst require a rebuild of the man page. +man: + rst2man.py README.rst > git-secrets.1 + +install: + @mkdir -p ${DESTDIR}${MANPREFIX} + @mkdir -p ${DESTDIR}${PREFIX}/bin + @cp -f git-secrets ${DESTDIR}${PREFIX}/bin + @cp -f git-secrets.1 ${DESTDIR}${MANPREFIX} + +.PHONY: help test man diff --git a/git-secrets/NOTICE.txt b/git-secrets/NOTICE.txt new file mode 100644 index 00000000..a5e5da9b --- /dev/null +++ b/git-secrets/NOTICE.txt @@ -0,0 +1,6 @@ +git-secrets +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +bats +This product bundles bats, which is available under a "MIT" license. +For details, see test/bats. diff --git a/git-secrets/README.rst b/git-secrets/README.rst new file mode 100644 index 00000000..0cafccd8 --- /dev/null +++ b/git-secrets/README.rst @@ -0,0 +1,543 @@ +=========== +git-secrets +=========== + +------------------------------------------------------------------------------------------- +Prevents you from committing passwords and other sensitive information to a git repository. +------------------------------------------------------------------------------------------- + +.. contents:: :depth: 2 + +Synopsis +-------- + +:: + + git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [...] + git secrets --scan-history + git secrets --install [-f|--force] [] + git secrets --list [--global] + git secrets --add [-a|--allowed] [-l|--literal] [--global] + git secrets --add-provider [--global] [arguments...] + git secrets --register-aws [--global] + git secrets --aws-provider [] + + +Description +----------- + +``git-secrets`` scans commits, commit messages, and ``--no-ff`` merges to +prevent adding secrets into your git repositories. If a commit, +commit message, or any commit in a ``--no-ff`` merge history matches one of +your configured prohibited regular expression patterns, then the commit is +rejected. + + +Installing git-secrets +---------------------- + +``git-secrets`` must be placed somewhere in your PATH so that it is picked up +by ``git`` when running ``git secrets``. + +\*nix (Linux/macOS) +~~~~~~~~~~~~~~~~~~~ + +You can use the ``install`` target of the provided Makefile to install ``git secrets`` and the man page. +You can customize the install path using the PREFIX and MANPREFIX variables. + +:: + + make install + +Windows +~~~~~~~ + +Run the provided ``install.ps1`` powershell script. This will copy the needed files +to an installation directory (``%USERPROFILE%/.git-secrets`` by default) and add +the directory to the current user ``PATH``. + +:: + + PS > ./install.ps1 + +Homebrew (for macOS users) +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + brew install git-secrets + +.. warning:: + + **You're not done yet! You MUST install the git hooks for every repo that + you wish to use with** ``git secrets --install``. + +Here's a quick example of how to ensure a git repository is scanned for secrets +on each commit:: + + cd /path/to/my/repo + git secrets --install + git secrets --register-aws + + +Advanced configuration +---------------------- + +Add a configuration template if you want to add hooks to all repositories you +initialize or clone in the future. + +:: + + git secrets --register-aws --global + + +Add hooks to all your local repositories. + +:: + + git secrets --install ~/.git-templates/git-secrets + git config --global init.templateDir ~/.git-templates/git-secrets + + +Add custom providers to scan for security credentials. + +:: + + git secrets --add-provider -- cat /path/to/secret/file/patterns + + +Before making public a repository +--------------------------------- + +With git-secrets is also possible to scan a repository including all revisions: + +:: + + git secrets --scan-history + + +Options +------- + +Operation Modes +~~~~~~~~~~~~~~~ + +Each of these options must appear first on the command line. + +``--install`` + Installs git hooks for a repository. Once the hooks are installed for a git + repository, commits and non-fast-forward merges for that repository will be prevented + from committing secrets. + +``--scan`` + Scans one or more files for secrets. When a file contains a secret, the + matched text from the file being scanned will be written to stdout and the + script will exit with a non-zero status. Each matched line will be written with + the name of the file that matched, a colon, the line number that matched, + a colon, and then the line of text that matched. If no files are provided, + all files returned by ``git ls-files`` are scanned. + +``--scan-history`` + Scans repository including all revisions. When a file contains a secret, the + matched text from the file being scanned will be written to stdout and the + script will exit with a non-zero status. Each matched line will be written with + the name of the file that matched, a colon, the line number that matched, + a colon, and then the line of text that matched. + +``--list`` + Lists the ``git-secrets`` configuration for the current repo or in the global + git config. + +``--add`` + Adds a prohibited or allowed pattern. + +``--add-provider`` + Registers a secret provider. Secret providers are executables that when + invoked output prohibited patterns that ``git-secrets`` should treat as + prohibited. + +``--register-aws`` + Adds common AWS patterns to the git config and ensures that keys present + in ``~/.aws/credentials`` are not found in any commit. The following + checks are added: + + - AWS Access Key IDs via ``(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}`` + - AWS Secret Access Key assignments via ":" or "=" surrounded by optional + quotes + - AWS account ID assignments via ":" or "=" surrounded by optional quotes + - Allowed patterns for example AWS keys (``AKIAIOSFODNN7EXAMPLE`` and + ``wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY``) + - Known credentials from ``~/.aws/credentials`` + + .. note:: + + While the patterns registered by this command should catch most + instances of AWS credentials, these patterns are **not** guaranteed to + catch them **all**. ``git-secrets`` should be used as an extra means of + insurance -- you still need to do your due diligence to ensure that you + do not commit credentials to a repository. + +``--aws-provider`` + Secret provider that outputs credentials found in an INI file. You can + optionally provide the path to an INI file. + + +Options for ``--install`` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +``-f, --force`` + Overwrites existing hooks if present. + +```` + When provided, installs git hooks to the given directory. The current + directory is assumed if ```` is not provided. + + If the provided ```` is not in a git repository, the + directory will be created and hooks will be placed in + ``/hooks``. This can be useful for creating git template + directories using with ``git init --template ``. + + You can run ``git init`` on a repository that has already been initialized. + From the `git init documentation `_: + + From the git documentation: Running ``git init`` in an existing repository + is safe. It will not overwrite things that are already there. The + primary reason for rerunning ``git init`` is to pick up newly added + templates (or to move the repository to another place if + ``--separate-git-dir`` is given). + + The following git hooks are installed: + + 1. ``pre-commit``: Used to check if any of the files changed in the commit + use prohibited patterns. + 2. ``commit-msg``: Used to determine if a commit message contains a + prohibited patterns. + 3. ``prepare-commit-msg``: Used to determine if a merge commit will + introduce a history that contains a prohibited pattern at any point. + Please note that this hook is only invoked for non fast-forward merges. + + .. note:: + + Git only allows a single script to be executed per hook. If the + repository contains Debian-style subdirectories like ``pre-commit.d`` + and ``commit-msg.d``, then the git hooks will be installed into these + directories, which assumes that you've configured the corresponding + hooks to execute all of the scripts found in these directories. If + these git subdirectories are not present, then the git hooks will be + installed to the git repo's ``.git/hooks`` directory. + + +Examples +^^^^^^^^ + +Install git hooks to the current directory:: + + cd /path/to/my/repository + git secrets --install + +Install git hooks to a repository other than the current directory:: + + git secrets --install /path/to/my/repository + +Create a git template that has ``git-secrets`` installed, and then copy that +template into a git repository:: + + git secrets --install ~/.git-templates/git-secrets + git init --template ~/.git-templates/git-secrets + +Overwrite existing hooks if present:: + + git secrets --install -f + + +Options for ``--scan`` +~~~~~~~~~~~~~~~~~~~~~~ + +``-r, --recursive`` + Scans the given files recursively. If a directory is encountered, the + directory will be scanned. If ``-r`` is not provided, directories will be + ignored. + + ``-r`` cannot be used alongside ``--cached``, ``--no-index``, or + ``--untracked``. + +``--cached`` + Searches blobs registered in the index file. + +``--no-index`` + Searches files in the current directory that is not managed by git. + +``--untracked`` + In addition to searching in the tracked files in the working tree, + ``--scan`` also in untracked files. + +``...`` + The path to one or more files on disk to scan for secrets. + + If no files are provided, all files returned by ``git ls-files`` are + scanned. + + +Examples +^^^^^^^^ + +Scan all files in the repo:: + + git secrets --scan + +Scans a single file for secrets:: + + git secrets --scan /path/to/file + +Scans a directory recursively for secrets:: + + git secrets --scan -r /path/to/directory + +Scans multiple files for secrets:: + + git secrets --scan /path/to/file /path/to/other/file + +You can scan by globbing:: + + git secrets --scan /path/to/directory/* + +Scan from stdin:: + + echo 'hello!' | git secrets --scan - + + +Options for ``--list`` +~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Lists only git-secrets configuration in the global git config. + + +Options for ``--add`` +~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds patterns to the global git config + +``-l, --literal`` + Escapes special regular expression characters in the provided pattern so + that the pattern is searched for literally. + +``-a, --allowed`` + Mark the pattern as allowed instead of prohibited. Allowed patterns are + used to filter out false positives. + +```` + The regex pattern to search. + + +Examples +^^^^^^^^ + +Adds a prohibited pattern to the current repo:: + + git secrets --add '[A-Z0-9]{20}' + +Adds a prohibited pattern to the global git config:: + + git secrets --add --global '[A-Z0-9]{20}' + +Adds a string that is scanned for literally (``+`` is escaped):: + + git secrets --add --literal 'foo+bar' + +Add an allowed pattern:: + + git secrets --add -a 'allowed pattern' + + +Options for ``--register-aws`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds AWS specific configuration variables to the global git config. + + +Options for ``--aws-provider`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``[]`` + If provided, specifies the custom path to an INI file to scan. If not + provided, ``~/.aws/credentials`` is assumed. + + +Options for ``--add-provider`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``--global`` + Adds the provider to the global git config. + +```` + Provider command to invoke. When invoked the command is expected to write + prohibited patterns separated by new lines to stdout. Any extra arguments + provided are passed on to the command. + + +Examples +^^^^^^^^ + +Registers a secret provider with arguments:: + + git secrets --add-provider -- git secrets --aws-provider + +Cats secrets out of a file:: + + git secrets --add-provider -- cat /path/to/secret/file/patterns + + +Defining prohibited patterns +---------------------------- + +``egrep``-compatible regular expressions are used to determine if a commit or +commit message contains any prohibited patterns. These regular expressions are +defined using the ``git config`` command. It is important to note that +different systems use different versions of egrep. For example, when running on +macOS, you will use a different version of ``egrep`` than when running on something +like Ubuntu (BSD vs GNU). + +You can add prohibited regular expression patterns to your git config using +``git secrets --add ``. + + +Ignoring false positives +------------------------ + +Sometimes a regular expression might match false positives. For example, git +commit SHAs look a lot like AWS access keys. You can specify many different +regular expression patterns as false positives using the following command: + +:: + + git secrets --add --allowed 'my regex pattern' + +You can also add regular expressions patterns to filter false positives to a +``.gitallowed`` file located in the repository's root directory. Lines starting +with ``#`` are skipped (comment line) and empty lines are also skipped. + +First, git-secrets will extract all lines from a file that contain a prohibited +match. Included in the matched results will be the full path to the name of +the file that was matched, followed by ':', followed by the line number that was +matched, followed by the entire line from the file that was matched by a secret +pattern. Then, if you've defined allowed regular expressions, git-secrets will +check to see if all of the matched lines match at least one of your registered +allowed regular expressions. If all of the lines that were flagged as secret +are canceled out by an allowed match, then the subject text does not contain +any secrets. If any of the matched lines are not matched by an allowed regular +expression, then git-secrets will fail the commit/merge/message. + +.. important:: + + Just as it is a bad practice to add prohibited patterns that are too + greedy, it is also a bad practice to add allowed patterns that are too + forgiving. Be sure to test out your patterns using ad-hoc calls to + ``git secrets --scan $filename`` to ensure they are working as intended. + + +Secret providers +---------------- + +Sometimes you want to check for an exact pattern match against a set of known +secrets. For example, you might want to ensure that no credentials present in +``~/.aws/credentials`` ever show up in a commit. In these cases, it's better to +leave these secrets in one location rather than spread them out across git +repositories in git configs. You can use "secret providers" to fetch these +types of credentials. A secret provider is an executable that when invoked +outputs prohibited patterns separated by new lines. + +You can add secret providers using the ``--add-provider`` command:: + + git secrets --add-provider -- git secrets --aws-provider + +Notice the use of ``--``. This ensures that any arguments associated with the +provider are passed to the provider each time it is invoked when scanning +for secrets. + + +Example walkthrough +------------------- + +Let's take a look at an example. Given the following subject text (stored in +``/tmp/example``):: + + This is a test! + password=ex@mplepassword + password=****** + More test... + +And the following registered patterns: + +:: + + git secrets --add 'password\s*=\s*.+' + git secrets --add --allowed --literal 'ex@mplepassword' + +Running ``git secrets --scan /tmp/example``, the result will +result in the following error output:: + + /tmp/example:3:password=****** + + [ERROR] Matched prohibited pattern + + Possible mitigations: + - Mark false positives as allowed using: git config --add secrets.allowed ... + - List your configured patterns: git config --get-all secrets.patterns + - List your configured allowed patterns: git config --get-all secrets.allowed + - Use --no-verify if this is a one-time false positive + +Breaking this down, the prohibited pattern value of ``password\s*=\s*.+`` will +match the following lines:: + + /tmp/example:2:password=ex@mplepassword + /tmp/example:3:password=****** + +...But the first match will be filtered out due to the fact that it matches the +allowed regular expression of ``ex@mplepassword``. Because there is still a +remaining line that did not match, it is considered a secret. + +Because that matching lines are placed on lines that start with the filename +and line number (e.g., ``/tmp/example:3:...``), you can create allowed +patterns that take filenames and line numbers into account in the regular +expression. For example, you could whitelist an entire file using something +like:: + + git secrets --add --allowed '/tmp/example:.*' + git secrets --scan /tmp/example && echo $? + # Outputs: 0 + +Alternatively, you could allow a specific line number of a file if that +line is unlikely to change using something like the following: + +:: + + git secrets --add --allowed '/tmp/example:3:.*' + git secrets --scan /tmp/example && echo $? + # Outputs: 0 + +Keep this in mind when creating allowed patterns to ensure that your allowed +patterns are not inadvertently matched due to the fact that the filename is +included in the subject text that allowed patterns are matched against. + + +Skipping validation +------------------- + +Use the ``--no-verify`` option in the event of a false positive match in a +commit, merge, or commit message. This will skip the execution of the +git hook and allow you to make the commit or merge. + + +About +------ + +- Author: `Michael Dowling `_ +- Issue tracker: This project's source code and issue tracker can be found at + `https://github.com/awslabs/git-secrets `_ +- Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for + providing suggestions and feedback. + +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/git-secrets/git-secrets b/git-secrets/git-secrets new file mode 100755 index 00000000..11be1537 --- /dev/null +++ b/git-secrets/git-secrets @@ -0,0 +1,357 @@ +#!/usr/bin/env bash +# Copyright 2010-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). +# You may not use this file except in compliance with the License. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0 +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +NONGIT_OK=1 OPTIONS_SPEC="\ +git secrets --scan [-r|--recursive] [--cached] [--no-index] [--untracked] [...] +git secrets --scan-history +git secrets --install [-f|--force] [] +git secrets --list [--global] +git secrets --add [-a|--allowed] [-l|--literal] [--global] +git secrets --add-provider [--global] [arguments...] +git secrets --register-aws [--global] +git secrets --aws-provider [] +-- +scan Scans for prohibited patterns +scan-history Scans repo for prohibited patterns +install Installs git hooks for Git repository or Git template directory +list Lists secret patterns +add Adds a prohibited or allowed pattern, ensuring to de-dupe with existing patterns +add-provider Adds a secret provider that when called outputs secret patterns on new lines +aws-provider Secret provider that outputs credentials found in an ini file +register-aws Adds common AWS patterns to the git config and scans for ~/.aws/credentials +r,recursive --scan scans directories recursively +cached --scan scans searches blobs registered in the index file +no-index --scan searches files in the current directory that is not managed by Git +untracked In addition to searching in the tracked files in the working tree, --scan also in untracked files +f,force --install overwrites hooks if the hook already exists +l,literal --add and --add-allowed patterns are escaped so that they are literal +a,allowed --add adds an allowed pattern instead of a prohibited pattern +global Uses the --global git config +commit_msg_hook* commit-msg hook (internal only) +pre_commit_hook* pre-commit hook (internal only) +prepare_commit_msg_hook* prepare-commit-msg hook (internal only)" + +# Include the git setup script. This parses and normalized CLI arguments. +. "$(git --exec-path)"/git-sh-setup + +load_patterns() { + git config --get-all secrets.patterns + # Execute each provider and use their output to build up patterns + git config --get-all secrets.providers | while read -r cmd; do + # Only split words on '\n\t ' and strip "\r" from the output to account + # for carriage returns being added on Windows systems. Note that this + # trimming is done before the test to ensure that the string is not empty. + local result="$(export IFS=$'\n\t '; $cmd | tr -d $'\r')" + # Do not add empty lines from providers as that would match everything. + if [ -n "${result}" ]; then + echo "${result}" + fi + done +} + +load_allowed() { + git config --get-all secrets.allowed + local gitallowed="$(git rev-parse --show-toplevel)/.gitallowed" + if [ -e "$gitallowed" ]; then + cat $gitallowed | awk 'NF && $1!~/^#/' + fi +} + +# load patterns and combine them with | +load_combined_patterns() { + local patterns=$(load_patterns) + local combined_patterns='' + for pattern in $patterns; do + combined_patterns=${combined_patterns}${pattern}"|" + done + combined_patterns=${combined_patterns%?} + echo $combined_patterns +} + +# Scans files or a repo using patterns. +scan() { + local files=("${@}") options="" + [ "${SCAN_CACHED}" == 1 ] && options+="--cached" + [ "${SCAN_UNTRACKED}" == 1 ] && options+=" --untracked" + [ "${SCAN_NO_INDEX}" == 1 ] && options+=" --no-index" + # Scan using git-grep if there are no files or if git options are applied. + if [ ${#files[@]} -eq 0 ] || [ ! -z "${options}" ]; then + output=$(git_grep $options "${files[@]}") + else + output=$(regular_grep "${files[@]}") + fi + process_output $? "${output}" +} + +# Scans through history using patterns +scan_history() { + # git log does not support multiple patterns, so we need to combine them + local combined_patterns=$(load_combined_patterns) + [ -z "${combined_patterns}" ] && return 0 + # Looks for differences matching the patterns, reduces the number of revisions to scan + local to_scan=$(git log --all -G"${combined_patterns}" --pretty=%H) + # Scan through revisions with findings to normalize output + output=$(GREP_OPTIONS= LC_ALL=C git grep -nwHEI "${combined_patterns}" $to_scan) + process_output $? "${output}" +} + +# Performs a git-grep, taking into account patterns and options. +# Note: this function returns 1 on success, 0 on error. +git_grep() { + local options="$1"; shift + local files=("${@}") combined_patterns=$(load_combined_patterns) + + [ -z "${combined_patterns}" ] && return 1 + GREP_OPTIONS= LC_ALL=C git grep -nwHEI ${options} "${combined_patterns}" -- "${files[@]}" +} + +# Performs a regular grep, taking into account patterns and recursion. +# Note: this function returns 1 on success, 0 on error. +regular_grep() { + local files=("${@}") patterns=$(load_patterns) action='skip' + [ -z "${patterns}" ] && return 1 + [ ${RECURSIVE} -eq 1 ] && action="recurse" + GREP_OPTIONS= LC_ALL=C grep -d "${action}" -nwHEI "${patterns}" "${files[@]}" +} + +# Process the given status ($1) and output variables ($2). +# Takes into account allowed patterns, and if a bad match is found, +# prints an error message and exits 1. +process_output() { + local status="$1" output="$2" + local allowed=$(load_allowed) + case "$status" in + 0) + [ -z "${allowed}" ] && echo "${output}" >&2 && return 1 + # Determine with a negative grep if the found matches are allowed + echo "${output}" | GREP_OPTIONS= LC_ALL=C grep -Ev "${allowed}" >&2 \ + && return 1 || return 0 + ;; + 1) return 0 ;; + *) exit $status + esac +} + +# Calls the given scanning function at $1, shifts, and passes to it $@. +# Exit 0 if success, otherwise exit 1 with error message. +scan_with_fn_or_die() { + local fn="$1"; shift + $fn "$@" && exit 0 + echo >&2 + echo "[ERROR] Matched one or more prohibited patterns" >&2 + echo >&2 + echo "Possible mitigations:" >&2 + echo "- Mark false positives as allowed using: git config --add secrets.allowed ..." >&2 + echo "- Mark false positives as allowed by adding regular expressions to .gitallowed at repository's root directory" >&2 + echo "- List your configured patterns: git config --get-all secrets.patterns" >&2 + echo "- List your configured allowed patterns: git config --get-all secrets.allowed" >&2 + echo "- List your configured allowed patterns in .gitallowed at repository's root directory" >&2 + echo "- Use --no-verify if this is a one-time false positive" >&2 + exit 1 +} + +# Scans a commit message, passed in the path to a file. +commit_msg_hook() { + scan_with_fn_or_die "scan" "$1" +} + +# Scans all files that are about to be committed. +pre_commit_hook() { + SCAN_CACHED=1 + local files=() file found_match=0 rev="4b825dc642cb6eb9a060e54bf8d69288fbee4904" + # Diff against HEAD if this is not the first commit in the repo. + git rev-parse --verify HEAD >/dev/null 2>&1 && rev="HEAD" + # Filter out deleted files using --diff-filter + while IFS= read -r file; do + [ -n "$file" ] && files+=("$file") + done <<< "$(git diff-index --diff-filter 'ACMU' --name-only --cached $rev --)" + scan_with_fn_or_die "scan" "${files[@]}" +} + +# Determines if merging in a commit will introduce tainted history. +prepare_commit_msg_hook() { + case "$2,$3" in + merge,) + local git_head=$(env | grep GITHEAD) # e.g. GITHEAD_=release/1.43 + local sha="${git_head##*=}" # Get just the SHA + local branch=$(git symbolic-ref HEAD) # e.g. refs/heads/master + local dest="${branch#refs/heads/}" # cut out "refs/heads" + git log "${dest}".."${sha}" -p | scan_with_fn_or_die "scan" - + ;; + esac +} + +install_hook() { + local path="$1" hook="$2" cmd="$3" dest + # Determines the approriate path for a hook to be installed + if [ -d "${path}/hooks/${hook}.d" ]; then + dest="${path}/hooks/${hook}.d/git-secrets" + else + dest="${path}/hooks/${hook}" + fi + [ -f "${dest}" ] && [ "${FORCE}" -ne 1 ] \ + && die "${dest} already exists. Use -f to force" + echo "#!/usr/bin/env bash" > "${dest}" + echo "git secrets --${cmd} -- \"\$@\"" >> "${dest}" + chmod +x "${dest}" + say "$(tput setaf 2)✓$(tput sgr 0) Installed ${hook} hook to ${dest}" +} + +install_all_hooks() { + install_hook "$1" "commit-msg" "commit_msg_hook" + install_hook "$1" "pre-commit" "pre_commit_hook" + install_hook "$1" "prepare-commit-msg" "prepare_commit_msg_hook" +} + +# Adds a git config pattern, ensuring to de-dupe +add_config() { + local key="$1"; shift + local value="$@" + if [ ${LITERAL} -eq 1 ]; then + value=$(sed 's/[\.|$(){}?+*^]/\\&/g' <<< "${value}") + fi + if [ ${GLOBAL} -eq 1 ]; then + git config --global --get-all $key | grep -Fq "${value}" && return 1 + git config --global --add "${key}" "${value}" + else + git config --get-all $key | grep -Fq "${value}" && return 1 + git config --add "${key}" "${value}" + fi +} + +register_aws() { + # Reusable regex patterns + local aws="(AWS|aws|Aws)?_?" quote="(\"|')" connect="\s*(:|=>|=)\s*" + local opt_quote="${quote}?" + add_config 'secrets.providers' 'git secrets --aws-provider' + add_config 'secrets.patterns' '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' + add_config 'secrets.patterns' "${opt_quote}${aws}(SECRET|secret|Secret)?_?(ACCESS|access|Access)?_?(KEY|key|Key)${opt_quote}${connect}${opt_quote}[A-Za-z0-9/\+=]{40}${opt_quote}" + add_config 'secrets.patterns' "${opt_quote}${aws}(ACCOUNT|account|Account)_?(ID|id|Id)?${opt_quote}${connect}${opt_quote}[0-9]{4}\-?[0-9]{4}\-?[0-9]{4}${opt_quote}" + add_config 'secrets.allowed' 'AKIAIOSFODNN7EXAMPLE' + add_config 'secrets.allowed' "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" + + if [[ $? == 0 ]]; then + echo 'OK' + fi + + exit $? +} + +aws_provider() { + local fi="$1" + [ -z "$fi" ] && fi=~/.aws/credentials + # Find keys and ensure that special characters are escaped. + if [ -f $fi ]; then + awk -F "=" '/aws_access_key_id|aws_secret_access_key/ {print $2}' $fi \ + | tr -d ' "' \ + | sed 's/[]\.|$(){}?+*^]/\\&/g' + fi +} + +# Ensures that the command is what was expected for an option. +assert_option_for_command() { + local expected_command="$1" + local option_name="$2" + if [ "${COMMAND}" != "${expected_command}" ]; then + die "${option_name} can only be supplied with the ${expected_command} subcommand" + fi +} + +declare COMMAND="$1" FORCE=0 RECURSIVE=0 LITERAL=0 GLOBAL=0 ALLOWED=0 +declare SCAN_CACHED=0 SCAN_NO_INDEX=0 SCAN_UNTRACKED=0 + +# Shift off the command name +shift 1 +while [ "$#" -ne 0 ]; do + case "$1" in + -f) + assert_option_for_command "--install" "-f|--force" + FORCE=1 + ;; + -r) + assert_option_for_command "--scan" "-r|--recursive" + RECURSIVE=1 + ;; + -a) + assert_option_for_command "--add" "-a|--allowed" + ALLOWED=1 + ;; + -l) + assert_option_for_command "--add" "-l|--literal" + LITERAL=1 + ;; + --cached) + assert_option_for_command "--scan" "--cached" + SCAN_CACHED=1 + ;; + --no-index) + assert_option_for_command "--scan" "--no-index" + SCAN_NO_INDEX=1 + ;; + --untracked) + assert_option_for_command "--scan" "--untracked" + SCAN_UNTRACKED=1 + ;; + --global) GLOBAL=1 ;; + --) shift; break ;; + esac + shift +done + +# Ensure that recursive is not applied with mutually exclusive options. +if [ ${RECURSIVE} -eq 1 ]; then + if [ ${SCAN_CACHED} -eq 1 ] \ + || [ ${SCAN_NO_INDEX} -eq 1 ] \ + || [ ${SCAN_UNTRACKED} -eq 1 ]; + then + die "-r|--recursive cannot be supplied with --cached, --no-index, or --untracked" + fi +fi + +case "${COMMAND}" in + -h|--help|--) "$0" -h; exit 0 ;; + --add-provider) add_config "secrets.providers" "$@" ;; + --register-aws) register_aws ;; + --aws-provider) aws_provider "$1" ;; + --commit_msg_hook|--pre_commit_hook|--prepare_commit_msg_hook) + ${COMMAND:2} "$@" + ;; + --add) + if [ ${ALLOWED} -eq 1 ]; then + add_config "secrets.allowed" "$1" + else + add_config "secrets.patterns" "$1" + fi + ;; + --scan) scan_with_fn_or_die "scan" "$@" ;; + --scan-history) scan_with_fn_or_die "scan_history" "$@" ;; + --list) + if [ ${GLOBAL} -eq 1 ]; then + git config --global --get-regex secrets.* + else + git config --get-regex secrets.* + fi + ;; + --install) + DIRECTORY="$1" + if [ -z "${DIRECTORY}" ]; then + DIRECTORY=$(git rev-parse --git-dir) || die "Not in a Git repository" + elif [ -d "${DIRECTORY}"/.git ]; then + DIRECTORY="${DIRECTORY}/.git" + fi + mkdir -p "${DIRECTORY}/hooks" || die "Could not create dir: ${DIRECTORY}" + install_all_hooks "${DIRECTORY}" + ;; + *) echo "Unknown option: ${COMMAND}" && "$0" -h ;; +esac diff --git a/git-secrets/git-secrets.1 b/git-secrets/git-secrets.1 new file mode 100644 index 00000000..27f6ff4c --- /dev/null +++ b/git-secrets/git-secrets.1 @@ -0,0 +1,843 @@ +.\" Man page generated from reStructuredText. +. +.TH GIT-SECRETS "" "" "" +.SH NAME +git-secrets \- Prevents you from committing passwords and other sensitive information to a git repository. +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.SS Contents +.INDENT 0.0 +.IP \(bu 2 +\fI\%Synopsis\fP +.IP \(bu 2 +\fI\%Description\fP +.IP \(bu 2 +\fI\%Installing git\-secrets\fP +.INDENT 2.0 +.IP \(bu 2 +\fI\%*nix (Linux/macOS)\fP +.IP \(bu 2 +\fI\%Windows\fP +.IP \(bu 2 +\fI\%Homebrew (for macOS users)\fP +.UNINDENT +.IP \(bu 2 +\fI\%Advanced configuration\fP +.IP \(bu 2 +\fI\%Before making public a repository\fP +.IP \(bu 2 +\fI\%Options\fP +.INDENT 2.0 +.IP \(bu 2 +\fI\%Operation Modes\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-install\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-scan\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-list\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-add\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-register\-aws\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-aws\-provider\fP\fP +.IP \(bu 2 +\fI\%Options for \fB\-\-add\-provider\fP\fP +.UNINDENT +.IP \(bu 2 +\fI\%Defining prohibited patterns\fP +.IP \(bu 2 +\fI\%Ignoring false positives\fP +.IP \(bu 2 +\fI\%Secret providers\fP +.IP \(bu 2 +\fI\%Example walkthrough\fP +.IP \(bu 2 +\fI\%Skipping validation\fP +.IP \(bu 2 +\fI\%About\fP +.UNINDENT +.SH SYNOPSIS +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan [\-r|\-\-recursive] [\-\-cached] [\-\-no\-index] [\-\-untracked] [...] +git secrets \-\-scan\-history +git secrets \-\-install [\-f|\-\-force] [] +git secrets \-\-list [\-\-global] +git secrets \-\-add [\-a|\-\-allowed] [\-l|\-\-literal] [\-\-global] +git secrets \-\-add\-provider [\-\-global] [arguments...] +git secrets \-\-register\-aws [\-\-global] +git secrets \-\-aws\-provider [] +.ft P +.fi +.UNINDENT +.UNINDENT +.SH DESCRIPTION +.sp +\fBgit\-secrets\fP scans commits, commit messages, and \fB\-\-no\-ff\fP merges to +prevent adding secrets into your git repositories. If a commit, +commit message, or any commit in a \fB\-\-no\-ff\fP merge history matches one of +your configured prohibited regular expression patterns, then the commit is +rejected. +.SH INSTALLING GIT-SECRETS +.sp +\fBgit\-secrets\fP must be placed somewhere in your PATH so that it is picked up +by \fBgit\fP when running \fBgit secrets\fP\&. +.SS *nix (Linux/macOS) +.IP "System Message: WARNING/2 (README.rst:, line 43)" +Title underline too short. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +\e*nix (Linux/macOS) +~~~~~~~~~~~~~~~~~ +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can use the \fBinstall\fP target of the provided Makefile to install \fBgit secrets\fP and the man page. +You can customize the install path using the PREFIX and MANPREFIX variables. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +make install +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Windows +.sp +Run the provided \fBinstall.ps1\fP powershell script. This will copy the needed files +to an installation directory (\fB%USERPROFILE%/.git\-secrets\fP by default) and add +the directory to the current user \fBPATH\fP\&. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +PS > ./install.ps1 +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Homebrew (for macOS users) +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +brew install git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +\fBWARNING:\fP +.INDENT 0.0 +.INDENT 3.5 +You\(aqre not done yet! You MUST install the git hooks for every repo that +you wish to use with \fBgit secrets \-\-install\fP\&. +.UNINDENT +.UNINDENT +.sp +Here\(aqs a quick example of how to ensure a git repository is scanned for secrets +on each commit: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +cd /path/to/my/repo +git secrets \-\-install +git secrets \-\-register\-aws +.ft P +.fi +.UNINDENT +.UNINDENT +.SH ADVANCED CONFIGURATION +.sp +Add a configuration template if you want to add hooks to all repositories you +initialize or clone in the future. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-register\-aws \-\-global +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add hooks to all your local repositories. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install ~/.git\-templates/git\-secrets +git config \-\-global init.templateDir ~/.git\-templates/git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add custom providers to scan for security credentials. +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns +.ft P +.fi +.UNINDENT +.UNINDENT +.SH BEFORE MAKING PUBLIC A REPOSITORY +.sp +With git\-secrets is also possible to scan a repository including all revisions: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan\-history +.ft P +.fi +.UNINDENT +.UNINDENT +.SH OPTIONS +.SS Operation Modes +.sp +Each of these options must appear first on the command line. +.INDENT 0.0 +.TP +.B \fB\-\-install\fP +Installs git hooks for a repository. Once the hooks are installed for a git +repository, commits and non\-fast\-forward merges for that repository will be prevented +from committing secrets. +.TP +.B \fB\-\-scan\fP +Scans one or more files for secrets. When a file contains a secret, the +matched text from the file being scanned will be written to stdout and the +script will exit with a non\-zero status. Each matched line will be written with +the name of the file that matched, a colon, the line number that matched, +a colon, and then the line of text that matched. If no files are provided, +all files returned by \fBgit ls\-files\fP are scanned. +.TP +.B \fB\-\-scan\-history\fP +Scans repository including all revisions. When a file contains a secret, the +matched text from the file being scanned will be written to stdout and the +script will exit with a non\-zero status. Each matched line will be written with +the name of the file that matched, a colon, the line number that matched, +a colon, and then the line of text that matched. +.TP +.B \fB\-\-list\fP +Lists the \fBgit\-secrets\fP configuration for the current repo or in the global +git config. +.TP +.B \fB\-\-add\fP +Adds a prohibited or allowed pattern. +.TP +.B \fB\-\-add\-provider\fP +Registers a secret provider. Secret providers are executables that when +invoked output prohibited patterns that \fBgit\-secrets\fP should treat as +prohibited. +.TP +.B \fB\-\-register\-aws\fP +Adds common AWS patterns to the git config and ensures that keys present +in \fB~/.aws/credentials\fP are not found in any commit. The following +checks are added: +.INDENT 7.0 +.IP \(bu 2 +AWS Access Key IDs via \fB(A3T[A\-Z0\-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A\-Z0\-9]{16}\fP +.IP \(bu 2 +AWS Secret Access Key assignments via ":" or "=" surrounded by optional +quotes +.IP \(bu 2 +AWS account ID assignments via ":" or "=" surrounded by optional quotes +.IP \(bu 2 +Allowed patterns for example AWS keys (\fBAKIAIOSFODNN7EXAMPLE\fP and +\fBwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\fP) +.IP \(bu 2 +Known credentials from \fB~/.aws/credentials\fP +.UNINDENT +.sp +\fBNOTE:\fP +.INDENT 7.0 +.INDENT 3.5 +While the patterns registered by this command should catch most +instances of AWS credentials, these patterns are \fBnot\fP guaranteed to +catch them \fBall\fP\&. \fBgit\-secrets\fP should be used as an extra means of +insurance \-\- you still need to do your due diligence to ensure that you +do not commit credentials to a repository. +.UNINDENT +.UNINDENT +.TP +.B \fB\-\-aws\-provider\fP +Secret provider that outputs credentials found in an INI file. You can +optionally provide the path to an INI file. +.UNINDENT +.SS Options for \fB\-\-install\fP +.INDENT 0.0 +.TP +.B \fB\-f, \-\-force\fP +Overwrites existing hooks if present. +.TP +.B \fB\fP +When provided, installs git hooks to the given directory. The current +directory is assumed if \fB\fP is not provided. +.sp +If the provided \fB\fP is not in a git repository, the +directory will be created and hooks will be placed in +\fB/hooks\fP\&. This can be useful for creating git template +directories using with \fBgit init \-\-template \fP\&. +.sp +You can run \fBgit init\fP on a repository that has already been initialized. +From the \fI\%git init documentation\fP: +.INDENT 7.0 +.INDENT 3.5 +From the git documentation: Running \fBgit init\fP in an existing repository +is safe. It will not overwrite things that are already there. The +primary reason for rerunning \fBgit init\fP is to pick up newly added +templates (or to move the repository to another place if +\fB\-\-separate\-git\-dir\fP is given). +.UNINDENT +.UNINDENT +.sp +The following git hooks are installed: +.INDENT 7.0 +.IP 1. 3 +\fBpre\-commit\fP: Used to check if any of the files changed in the commit +use prohibited patterns. +.IP 2. 3 +\fBcommit\-msg\fP: Used to determine if a commit message contains a +prohibited patterns. +.IP 3. 3 +\fBprepare\-commit\-msg\fP: Used to determine if a merge commit will +introduce a history that contains a prohibited pattern at any point. +Please note that this hook is only invoked for non fast\-forward merges. +.UNINDENT +.sp +\fBNOTE:\fP +.INDENT 7.0 +.INDENT 3.5 +Git only allows a single script to be executed per hook. If the +repository contains Debian\-style subdirectories like \fBpre\-commit.d\fP +and \fBcommit\-msg.d\fP, then the git hooks will be installed into these +directories, which assumes that you\(aqve configured the corresponding +hooks to execute all of the scripts found in these directories. If +these git subdirectories are not present, then the git hooks will be +installed to the git repo\(aqs \fB\&.git/hooks\fP directory. +.UNINDENT +.UNINDENT +.UNINDENT +.SS Examples +.sp +Install git hooks to the current directory: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +cd /path/to/my/repository +git secrets \-\-install +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Install git hooks to a repository other than the current directory: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install /path/to/my/repository +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Create a git template that has \fBgit\-secrets\fP installed, and then copy that +template into a git repository: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install ~/.git\-templates/git\-secrets +git init \-\-template ~/.git\-templates/git\-secrets +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Overwrite existing hooks if present: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-install \-f +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-scan\fP +.INDENT 0.0 +.TP +.B \fB\-r, \-\-recursive\fP +Scans the given files recursively. If a directory is encountered, the +directory will be scanned. If \fB\-r\fP is not provided, directories will be +ignored. +.sp +\fB\-r\fP cannot be used alongside \fB\-\-cached\fP, \fB\-\-no\-index\fP, or +\fB\-\-untracked\fP\&. +.TP +.B \fB\-\-cached\fP +Searches blobs registered in the index file. +.TP +.B \fB\-\-no\-index\fP +Searches files in the current directory that is not managed by git. +.TP +.B \fB\-\-untracked\fP +In addition to searching in the tracked files in the working tree, +\fB\-\-scan\fP also in untracked files. +.TP +.B \fB...\fP +The path to one or more files on disk to scan for secrets. +.sp +If no files are provided, all files returned by \fBgit ls\-files\fP are +scanned. +.UNINDENT +.SS Examples +.sp +Scan all files in the repo: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans a single file for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/file +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans a directory recursively for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan \-r /path/to/directory +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scans multiple files for secrets: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/file /path/to/other/file +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can scan by globbing: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-scan /path/to/directory/* +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Scan from stdin: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +echo \(aqhello!\(aq | git secrets \-\-scan \- +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-list\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Lists only git\-secrets configuration in the global git config. +.UNINDENT +.SS Options for \fB\-\-add\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds patterns to the global git config +.TP +.B \fB\-l, \-\-literal\fP +Escapes special regular expression characters in the provided pattern so +that the pattern is searched for literally. +.TP +.B \fB\-a, \-\-allowed\fP +Mark the pattern as allowed instead of prohibited. Allowed patterns are +used to filter out false positives. +.TP +.B \fB\fP +The regex pattern to search. +.UNINDENT +.SS Examples +.sp +Adds a prohibited pattern to the current repo: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \(aq[A\-Z0\-9]{20}\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Adds a prohibited pattern to the global git config: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-global \(aq[A\-Z0\-9]{20}\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Adds a string that is scanned for literally (\fB+\fP is escaped): +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-literal \(aqfoo+bar\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Add an allowed pattern: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-a \(aqallowed pattern\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.SS Options for \fB\-\-register\-aws\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds AWS specific configuration variables to the global git config. +.UNINDENT +.SS Options for \fB\-\-aws\-provider\fP +.INDENT 0.0 +.TP +.B \fB[]\fP +If provided, specifies the custom path to an INI file to scan. If not +provided, \fB~/.aws/credentials\fP is assumed. +.UNINDENT +.SS Options for \fB\-\-add\-provider\fP +.INDENT 0.0 +.TP +.B \fB\-\-global\fP +Adds the provider to the global git config. +.TP +.B \fB\fP +Provider command to invoke. When invoked the command is expected to write +prohibited patterns separated by new lines to stdout. Any extra arguments +provided are passed on to the command. +.UNINDENT +.SS Examples +.sp +Registers a secret provider with arguments: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Cats secrets out of a file: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- cat /path/to/secret/file/patterns +.ft P +.fi +.UNINDENT +.UNINDENT +.SH DEFINING PROHIBITED PATTERNS +.sp +\fBegrep\fP\-compatible regular expressions are used to determine if a commit or +commit message contains any prohibited patterns. These regular expressions are +defined using the \fBgit config\fP command. It is important to note that +different systems use different versions of egrep. For example, when running on +macOS, you will use a different version of \fBegrep\fP than when running on something +like Ubuntu (BSD vs GNU). +.sp +You can add prohibited regular expression patterns to your git config using +\fBgit secrets \-\-add \fP\&. +.SH IGNORING FALSE POSITIVES +.sp +Sometimes a regular expression might match false positives. For example, git +commit SHAs look a lot like AWS access keys. You can specify many different +regular expression patterns as false positives using the following command: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aqmy regex pattern\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +You can also add regular expressions patterns to filter false positives to a +\fB\&.gitallowed\fP file located in the repository\(aqs root directory. Lines starting +with \fB#\fP are skipped (comment line) and empty lines are also skipped. +.sp +First, git\-secrets will extract all lines from a file that contain a prohibited +match. Included in the matched results will be the full path to the name of +the file that was matched, followed by \(aq:\(aq, followed by the line number that was +matched, followed by the entire line from the file that was matched by a secret +pattern. Then, if you\(aqve defined allowed regular expressions, git\-secrets will +check to see if all of the matched lines match at least one of your registered +allowed regular expressions. If all of the lines that were flagged as secret +are canceled out by an allowed match, then the subject text does not contain +any secrets. If any of the matched lines are not matched by an allowed regular +expression, then git\-secrets will fail the commit/merge/message. +.sp +\fBIMPORTANT:\fP +.INDENT 0.0 +.INDENT 3.5 +Just as it is a bad practice to add prohibited patterns that are too +greedy, it is also a bad practice to add allowed patterns that are too +forgiving. Be sure to test out your patterns using ad\-hoc calls to +\fBgit secrets \-\-scan $filename\fP to ensure they are working as intended. +.UNINDENT +.UNINDENT +.SH SECRET PROVIDERS +.sp +Sometimes you want to check for an exact pattern match against a set of known +secrets. For example, you might want to ensure that no credentials present in +\fB~/.aws/credentials\fP ever show up in a commit. In these cases, it\(aqs better to +leave these secrets in one location rather than spread them out across git +repositories in git configs. You can use "secret providers" to fetch these +types of credentials. A secret provider is an executable that when invoked +outputs prohibited patterns separated by new lines. +.sp +You can add secret providers using the \fB\-\-add\-provider\fP command: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add\-provider \-\- git secrets \-\-aws\-provider +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Notice the use of \fB\-\-\fP\&. This ensures that any arguments associated with the +provider are passed to the provider each time it is invoked when scanning +for secrets. +.SH EXAMPLE WALKTHROUGH +.sp +Let\(aqs take a look at an example. Given the following subject text (stored in +\fB/tmp/example\fP): +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +This is a test! +password=ex@mplepassword +password=****** +More test... +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +And the following registered patterns: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \(aqpassword\es*=\es*.+\(aq +git secrets \-\-add \-\-allowed \-\-literal \(aqex@mplepassword\(aq +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Running \fBgit secrets \-\-scan /tmp/example\fP, the result will +result in the following error output: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +/tmp/example:3:password=****** + +[ERROR] Matched prohibited pattern + +Possible mitigations: +\- Mark false positives as allowed using: git config \-\-add secrets.allowed ... +\- List your configured patterns: git config \-\-get\-all secrets.patterns +\- List your configured allowed patterns: git config \-\-get\-all secrets.allowed +\- Use \-\-no\-verify if this is a one\-time false positive +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Breaking this down, the prohibited pattern value of \fBpassword\es*=\es*.+\fP will +match the following lines: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +/tmp/example:2:password=ex@mplepassword +/tmp/example:3:password=****** +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +\&...But the first match will be filtered out due to the fact that it matches the +allowed regular expression of \fBex@mplepassword\fP\&. Because there is still a +remaining line that did not match, it is considered a secret. +.sp +Because that matching lines are placed on lines that start with the filename +and line number (e.g., \fB/tmp/example:3:...\fP), you can create allowed +patterns that take filenames and line numbers into account in the regular +expression. For example, you could whitelist an entire file using something +like: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aq/tmp/example:.*\(aq +git secrets \-\-scan /tmp/example && echo $? +# Outputs: 0 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Alternatively, you could allow a specific line number of a file if that +line is unlikely to change using something like the following: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +git secrets \-\-add \-\-allowed \(aq/tmp/example:3:.*\(aq +git secrets \-\-scan /tmp/example && echo $? +# Outputs: 0 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Keep this in mind when creating allowed patterns to ensure that your allowed +patterns are not inadvertently matched due to the fact that the filename is +included in the subject text that allowed patterns are matched against. +.SH SKIPPING VALIDATION +.sp +Use the \fB\-\-no\-verify\fP option in the event of a false positive match in a +commit, merge, or commit message. This will skip the execution of the +git hook and allow you to make the commit or merge. +.SH ABOUT +.INDENT 0.0 +.IP \(bu 2 +Author: \fI\%Michael Dowling\fP +.IP \(bu 2 +Issue tracker: This project\(aqs source code and issue tracker can be found at +\fI\%https://github.com/awslabs/git\-secrets\fP +.IP \(bu 2 +Special thanks to Adrian Vatchinsky and Ari Juels of Cornell University for +providing suggestions and feedback. +.UNINDENT +.sp +Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. +.\" Generated by docutils manpage writer. +. diff --git a/git-secrets/install.ps1 b/git-secrets/install.ps1 new file mode 100644 index 00000000..fbffbfa9 --- /dev/null +++ b/git-secrets/install.ps1 @@ -0,0 +1,48 @@ +Param([string]$InstallationDirectory = $($Env:USERPROFILE + "\.git-secrets")) + +Write-Host "Checking to see if installation directory already exists..." +if (-not (Test-Path $InstallationDirectory)) +{ + Write-Host "Creating installation directory." + New-Item -ItemType Directory -Path $InstallationDirectory | Out-Null +} +else +{ + Write-Host "Installation directory already exists." +} + +Write-Host "Copying files." +Copy-Item ./git-secrets -Destination $InstallationDirectory -Force +Copy-Item ./git-secrets.1 -Destination $InstallationDirectory -Force + +Write-Host "Checking if directory already exists in Path..." +$currentPath = [Environment]::GetEnvironmentVariable("PATH", "User") +if ($currentPath -notlike "*$InstallationDirectory*") +{ + Write-Host "Adding to path." + $newPath = $currentPath + if(-not ($newPath.EndsWith(";"))) + { + $newPath = $newPath + ";" + } + $newPath = $newPath + $InstallationDirectory + [Environment]::SetEnvironmentVariable("PATH", $newPath, "User") +} +else +{ + Write-Host "Already in Path." +} + +# Adding to Session +Write-Host "Adding to user session." +$currentSessionPath = $Env:Path +if ($currentSessionPath -notlike "*$InstallationDirectory*") +{ + if(-not ($currentSessionPath.EndsWith(";"))) + { + $currentSessionPath = $currentSessionPath + ";" + } + $Env:Path = $currentSessionPath + $InstallationDirectory +} + +Write-Host "Done." \ No newline at end of file diff --git a/git-secrets/test/bats/LICENSE b/git-secrets/test/bats/LICENSE new file mode 100644 index 00000000..bac4eb29 --- /dev/null +++ b/git-secrets/test/bats/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2014 Sam Stephenson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/git-secrets/test/bats/bin/bats b/git-secrets/test/bats/bin/bats new file mode 120000 index 00000000..a50a884e --- /dev/null +++ b/git-secrets/test/bats/bin/bats @@ -0,0 +1 @@ +../libexec/bats \ No newline at end of file diff --git a/git-secrets/test/bats/libexec/bats b/git-secrets/test/bats/libexec/bats new file mode 100755 index 00000000..71f392f7 --- /dev/null +++ b/git-secrets/test/bats/libexec/bats @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +set -e + +version() { + echo "Bats 0.4.0" +} + +usage() { + version + echo "Usage: bats [-c] [-p | -t] [ ...]" +} + +help() { + usage + echo + echo " is the path to a Bats test file, or the path to a directory" + echo " containing Bats test files." + echo + echo " -c, --count Count the number of test cases without running any tests" + echo " -h, --help Display this help message" + echo " -p, --pretty Show results in pretty format (default for terminals)" + echo " -t, --tap Show results in TAP format" + echo " -v, --version Display the version number" + echo + echo " For more information, see https://github.com/sstephenson/bats" + echo +} + +resolve_link() { + $(type -p greadlink readlink | head -1) "$1" +} + +abs_dirname() { + local cwd="$(pwd)" + local path="$1" + + while [ -n "$path" ]; do + cd "${path%/*}" + local name="${path##*/}" + path="$(resolve_link "$name" || true)" + done + + pwd + cd "$cwd" +} + +expand_path() { + { cd "$(dirname "$1")" 2>/dev/null + local dirname="$PWD" + cd "$OLDPWD" + echo "$dirname/$(basename "$1")" + } || echo "$1" +} + +BATS_LIBEXEC="$(abs_dirname "$0")" +export BATS_PREFIX="$(abs_dirname "$BATS_LIBEXEC")" +export BATS_CWD="$(abs_dirname .)" +export PATH="$BATS_LIBEXEC:$PATH" + +options=() +arguments=() +for arg in "$@"; do + if [ "${arg:0:1}" = "-" ]; then + if [ "${arg:1:1}" = "-" ]; then + options[${#options[*]}]="${arg:2}" + else + index=1 + while option="${arg:$index:1}"; do + [ -n "$option" ] || break + options[${#options[*]}]="$option" + let index+=1 + done + fi + else + arguments[${#arguments[*]}]="$arg" + fi +done + +unset count_flag pretty +[ -t 0 ] && [ -t 1 ] && pretty="1" +[ -n "$CI" ] && pretty="" + +for option in "${options[@]}"; do + case "$option" in + "h" | "help" ) + help + exit 0 + ;; + "v" | "version" ) + version + exit 0 + ;; + "c" | "count" ) + count_flag="-c" + ;; + "t" | "tap" ) + pretty="" + ;; + "p" | "pretty" ) + pretty="1" + ;; + * ) + usage >&2 + exit 1 + ;; + esac +done + +if [ "${#arguments[@]}" -eq 0 ]; then + usage >&2 + exit 1 +fi + +filenames=() +for filename in "${arguments[@]}"; do + if [ -d "$filename" ]; then + shopt -s nullglob + for suite_filename in "$(expand_path "$filename")"/*.bats; do + filenames["${#filenames[@]}"]="$suite_filename" + done + shopt -u nullglob + else + filenames["${#filenames[@]}"]="$(expand_path "$filename")" + fi +done + +if [ "${#filenames[@]}" -eq 1 ]; then + command="bats-exec-test" +else + command="bats-exec-suite" +fi + +if [ -n "$pretty" ]; then + extended_syntax_flag="-x" + formatter="bats-format-tap-stream" +else + extended_syntax_flag="" + formatter="cat" +fi + +set -o pipefail execfail +exec "$command" $count_flag $extended_syntax_flag "${filenames[@]}" | "$formatter" diff --git a/git-secrets/test/bats/libexec/bats-exec-suite b/git-secrets/test/bats/libexec/bats-exec-suite new file mode 100755 index 00000000..29ab255d --- /dev/null +++ b/git-secrets/test/bats/libexec/bats-exec-suite @@ -0,0 +1,55 @@ +#!/usr/bin/env bash +set -e + +count_only_flag="" +if [ "$1" = "-c" ]; then + count_only_flag=1 + shift +fi + +extended_syntax_flag="" +if [ "$1" = "-x" ]; then + extended_syntax_flag="-x" + shift +fi + +trap "kill 0; exit 1" int + +count=0 +for filename in "$@"; do + let count+="$(bats-exec-test -c "$filename")" +done + +if [ -n "$count_only_flag" ]; then + echo "$count" + exit +fi + +echo "1..$count" +status=0 +offset=0 +for filename in "$@"; do + index=0 + { + IFS= read -r # 1..n + while IFS= read -r line; do + case "$line" in + "begin "* ) + let index+=1 + echo "${line/ $index / $(($offset + $index)) }" + ;; + "ok "* | "not ok "* ) + [ -n "$extended_syntax_flag" ] || let index+=1 + echo "${line/ $index / $(($offset + $index)) }" + [ "${line:0:6}" != "not ok" ] || status=1 + ;; + * ) + echo "$line" + ;; + esac + done + } < <( bats-exec-test $extended_syntax_flag "$filename" ) + offset=$(($offset + $index)) +done + +exit "$status" diff --git a/git-secrets/test/bats/libexec/bats-exec-test b/git-secrets/test/bats/libexec/bats-exec-test new file mode 100755 index 00000000..8f3bd510 --- /dev/null +++ b/git-secrets/test/bats/libexec/bats-exec-test @@ -0,0 +1,346 @@ +#!/usr/bin/env bash +set -e +set -E +set -T + +BATS_COUNT_ONLY="" +if [ "$1" = "-c" ]; then + BATS_COUNT_ONLY=1 + shift +fi + +BATS_EXTENDED_SYNTAX="" +if [ "$1" = "-x" ]; then + BATS_EXTENDED_SYNTAX="$1" + shift +fi + +BATS_TEST_FILENAME="$1" +if [ -z "$BATS_TEST_FILENAME" ]; then + echo "usage: bats-exec " >&2 + exit 1 +elif [ ! -f "$BATS_TEST_FILENAME" ]; then + echo "bats: $BATS_TEST_FILENAME does not exist" >&2 + exit 1 +else + shift +fi + +BATS_TEST_DIRNAME="$(dirname "$BATS_TEST_FILENAME")" +BATS_TEST_NAMES=() + +load() { + local name="$1" + local filename + + if [ "${name:0:1}" = "/" ]; then + filename="${name}" + else + filename="$BATS_TEST_DIRNAME/${name}.bash" + fi + + [ -f "$filename" ] || { + echo "bats: $filename does not exist" >&2 + exit 1 + } + + source "${filename}" +} + +run() { + local e E T oldIFS + [[ ! "$-" =~ e ]] || e=1 + [[ ! "$-" =~ E ]] || E=1 + [[ ! "$-" =~ T ]] || T=1 + set +e + set +E + set +T + output="$("$@" 2>&1)" + status="$?" + oldIFS=$IFS + IFS=$'\n' lines=($output) + [ -z "$e" ] || set -e + [ -z "$E" ] || set -E + [ -z "$T" ] || set -T + IFS=$oldIFS +} + +setup() { + true +} + +teardown() { + true +} + +skip() { + BATS_TEST_SKIPPED=${1:-1} + BATS_TEST_COMPLETED=1 + exit 0 +} + +bats_test_begin() { + BATS_TEST_DESCRIPTION="$1" + if [ -n "$BATS_EXTENDED_SYNTAX" ]; then + echo "begin $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 + fi + setup +} + +bats_test_function() { + local test_name="$1" + BATS_TEST_NAMES["${#BATS_TEST_NAMES[@]}"]="$test_name" +} + +bats_capture_stack_trace() { + BATS_PREVIOUS_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) + BATS_CURRENT_STACK_TRACE=() + + local test_pattern=" $BATS_TEST_NAME $BATS_TEST_SOURCE" + local setup_pattern=" setup $BATS_TEST_SOURCE" + local teardown_pattern=" teardown $BATS_TEST_SOURCE" + + local frame + local index=1 + + while frame="$(caller "$index")"; do + BATS_CURRENT_STACK_TRACE["${#BATS_CURRENT_STACK_TRACE[@]}"]="$frame" + if [[ "$frame" = *"$test_pattern" || \ + "$frame" = *"$setup_pattern" || \ + "$frame" = *"$teardown_pattern" ]]; then + break + else + let index+=1 + fi + done + + BATS_SOURCE="$(bats_frame_filename "${BATS_CURRENT_STACK_TRACE[0]}")" + BATS_LINENO="$(bats_frame_lineno "${BATS_CURRENT_STACK_TRACE[0]}")" +} + +bats_print_stack_trace() { + local frame + local index=1 + local count="${#@}" + + for frame in "$@"; do + local filename="$(bats_trim_filename "$(bats_frame_filename "$frame")")" + local lineno="$(bats_frame_lineno "$frame")" + + if [ $index -eq 1 ]; then + echo -n "# (" + else + echo -n "# " + fi + + local fn="$(bats_frame_function "$frame")" + if [ "$fn" != "$BATS_TEST_NAME" ]; then + echo -n "from function \`$fn' " + fi + + if [ $index -eq $count ]; then + echo "in test file $filename, line $lineno)" + else + echo "in file $filename, line $lineno," + fi + + let index+=1 + done +} + +bats_print_failed_command() { + local frame="$1" + local status="$2" + local filename="$(bats_frame_filename "$frame")" + local lineno="$(bats_frame_lineno "$frame")" + + local failed_line="$(bats_extract_line "$filename" "$lineno")" + local failed_command="$(bats_strip_string "$failed_line")" + echo -n "# \`${failed_command}' " + + if [ $status -eq 1 ]; then + echo "failed" + else + echo "failed with status $status" + fi +} + +bats_frame_lineno() { + local frame="$1" + local lineno="${frame%% *}" + echo "$lineno" +} + +bats_frame_function() { + local frame="$1" + local rest="${frame#* }" + local fn="${rest%% *}" + echo "$fn" +} + +bats_frame_filename() { + local frame="$1" + local rest="${frame#* }" + local filename="${rest#* }" + + if [ "$filename" = "$BATS_TEST_SOURCE" ]; then + echo "$BATS_TEST_FILENAME" + else + echo "$filename" + fi +} + +bats_extract_line() { + local filename="$1" + local lineno="$2" + sed -n "${lineno}p" "$filename" +} + +bats_strip_string() { + local string="$1" + printf "%s" "$string" | sed -e "s/^[ "$'\t'"]*//" -e "s/[ "$'\t'"]*$//" +} + +bats_trim_filename() { + local filename="$1" + local length="${#BATS_CWD}" + + if [ "${filename:0:length+1}" = "${BATS_CWD}/" ]; then + echo "${filename:length+1}" + else + echo "$filename" + fi +} + +bats_debug_trap() { + if [ "$BASH_SOURCE" != "$1" ]; then + bats_capture_stack_trace + fi +} + +bats_error_trap() { + BATS_ERROR_STATUS="$?" + BATS_ERROR_STACK_TRACE=( "${BATS_PREVIOUS_STACK_TRACE[@]}" ) + trap - debug +} + +bats_teardown_trap() { + trap "bats_exit_trap" exit + local status=0 + teardown >>"$BATS_OUT" 2>&1 || status="$?" + + if [ $status -eq 0 ]; then + BATS_TEARDOWN_COMPLETED=1 + elif [ -n "$BATS_TEST_COMPLETED" ]; then + BATS_ERROR_STATUS="$status" + BATS_ERROR_STACK_TRACE=( "${BATS_CURRENT_STACK_TRACE[@]}" ) + fi + + bats_exit_trap +} + +bats_exit_trap() { + local status + local skipped + trap - err exit + + skipped="" + if [ -n "$BATS_TEST_SKIPPED" ]; then + skipped=" # skip" + if [ "1" != "$BATS_TEST_SKIPPED" ]; then + skipped+=" ($BATS_TEST_SKIPPED)" + fi + fi + + if [ -z "$BATS_TEST_COMPLETED" ] || [ -z "$BATS_TEARDOWN_COMPLETED" ]; then + echo "not ok $BATS_TEST_NUMBER $BATS_TEST_DESCRIPTION" >&3 + bats_print_stack_trace "${BATS_ERROR_STACK_TRACE[@]}" >&3 + bats_print_failed_command "${BATS_ERROR_STACK_TRACE[${#BATS_ERROR_STACK_TRACE[@]}-1]}" "$BATS_ERROR_STATUS" >&3 + sed -e "s/^/# /" < "$BATS_OUT" >&3 + status=1 + else + echo "ok ${BATS_TEST_NUMBER}${skipped} ${BATS_TEST_DESCRIPTION}" >&3 + status=0 + fi + + rm -f "$BATS_OUT" + exit "$status" +} + +bats_perform_tests() { + echo "1..$#" + test_number=1 + status=0 + for test_name in "$@"; do + "$0" $BATS_EXTENDED_SYNTAX "$BATS_TEST_FILENAME" "$test_name" "$test_number" || status=1 + let test_number+=1 + done + exit "$status" +} + +bats_perform_test() { + BATS_TEST_NAME="$1" + if [ "$(type -t "$BATS_TEST_NAME" || true)" = "function" ]; then + BATS_TEST_NUMBER="$2" + if [ -z "$BATS_TEST_NUMBER" ]; then + echo "1..1" + BATS_TEST_NUMBER="1" + fi + + BATS_TEST_COMPLETED="" + BATS_TEARDOWN_COMPLETED="" + trap "bats_debug_trap \"\$BASH_SOURCE\"" debug + trap "bats_error_trap" err + trap "bats_teardown_trap" exit + "$BATS_TEST_NAME" >>"$BATS_OUT" 2>&1 + BATS_TEST_COMPLETED=1 + + else + echo "bats: unknown test name \`$BATS_TEST_NAME'" >&2 + exit 1 + fi +} + +if [ -z "$TMPDIR" ]; then + BATS_TMPDIR="/tmp" +else + BATS_TMPDIR="${TMPDIR%/}" +fi + +BATS_TMPNAME="$BATS_TMPDIR/bats.$$" +BATS_PARENT_TMPNAME="$BATS_TMPDIR/bats.$PPID" +BATS_OUT="${BATS_TMPNAME}.out" + +bats_preprocess_source() { + BATS_TEST_SOURCE="${BATS_TMPNAME}.src" + { tr -d '\r' < "$BATS_TEST_FILENAME"; echo; } | bats-preprocess > "$BATS_TEST_SOURCE" + trap "bats_cleanup_preprocessed_source" err exit + trap "bats_cleanup_preprocessed_source; exit 1" int +} + +bats_cleanup_preprocessed_source() { + rm -f "$BATS_TEST_SOURCE" +} + +bats_evaluate_preprocessed_source() { + if [ -z "$BATS_TEST_SOURCE" ]; then + BATS_TEST_SOURCE="${BATS_PARENT_TMPNAME}.src" + fi + source "$BATS_TEST_SOURCE" +} + +exec 3<&1 + +if [ "$#" -eq 0 ]; then + bats_preprocess_source + bats_evaluate_preprocessed_source + + if [ -n "$BATS_COUNT_ONLY" ]; then + echo "${#BATS_TEST_NAMES[@]}" + else + bats_perform_tests "${BATS_TEST_NAMES[@]}" + fi +else + bats_evaluate_preprocessed_source + bats_perform_test "$@" +fi diff --git a/git-secrets/test/bats/libexec/bats-format-tap-stream b/git-secrets/test/bats/libexec/bats-format-tap-stream new file mode 100755 index 00000000..614768f4 --- /dev/null +++ b/git-secrets/test/bats/libexec/bats-format-tap-stream @@ -0,0 +1,165 @@ +#!/usr/bin/env bash +set -e + +# Just stream the TAP output (sans extended syntax) if tput is missing +command -v tput >/dev/null || exec grep -v "^begin " + +header_pattern='[0-9]+\.\.[0-9]+' +IFS= read -r header + +if [[ "$header" =~ $header_pattern ]]; then + count="${header:3}" + index=0 + failures=0 + skipped=0 + name="" + count_column_width=$(( ${#count} * 2 + 2 )) +else + # If the first line isn't a TAP plan, print it and pass the rest through + printf "%s\n" "$header" + exec cat +fi + +update_screen_width() { + screen_width="$(tput cols)" + count_column_left=$(( $screen_width - $count_column_width )) +} + +trap update_screen_width WINCH +update_screen_width + +begin() { + go_to_column 0 + printf_with_truncation $(( $count_column_left - 1 )) " %s" "$name" + clear_to_end_of_line + go_to_column $count_column_left + printf "%${#count}s/${count}" "$index" + go_to_column 1 +} + +pass() { + go_to_column 0 + printf " ✓ %s" "$name" + advance +} + +skip() { + local reason="$1" + [ -z "$reason" ] || reason=": $reason" + go_to_column 0 + printf " - %s (skipped%s)" "$name" "$reason" + advance +} + +fail() { + go_to_column 0 + set_color 1 bold + printf " ✗ %s" "$name" + advance +} + +log() { + set_color 1 + printf " %s\n" "$1" + clear_color +} + +summary() { + printf "\n%d test%s" "$count" "$(plural "$count")" + + printf ", %d failure%s" "$failures" "$(plural "$failures")" + + if [ "$skipped" -gt 0 ]; then + printf ", %d skipped" "$skipped" + fi + + printf "\n" +} + +printf_with_truncation() { + local width="$1" + shift + local string="$(printf "$@")" + + if [ "${#string}" -gt "$width" ]; then + printf "%s..." "${string:0:$(( $width - 4 ))}" + else + printf "%s" "$string" + fi +} + +go_to_column() { + local column="$1" + printf "\x1B[%dG" $(( $column + 1 )) +} + +clear_to_end_of_line() { + printf "\x1B[K" +} + +advance() { + clear_to_end_of_line + echo + clear_color +} + +set_color() { + local color="$1" + local weight="$2" + printf "\x1B[%d;%dm" $(( 30 + $color )) "$( [ "$weight" = "bold" ] && echo 1 || echo 22 )" +} + +clear_color() { + printf "\x1B[0m" +} + +plural() { + [ "$1" -eq 1 ] || echo "s" +} + +_buffer="" + +buffer() { + _buffer="${_buffer}$("$@")" +} + +flush() { + printf "%s" "$_buffer" + _buffer="" +} + +finish() { + flush + printf "\n" +} + +trap finish EXIT + +while IFS= read -r line; do + case "$line" in + "begin "* ) + let index+=1 + name="${line#* $index }" + buffer begin + flush + ;; + "ok "* ) + skip_expr="ok $index # skip (\(([^)]*)\))?" + if [[ "$line" =~ $skip_expr ]]; then + let skipped+=1 + buffer skip "${BASH_REMATCH[2]}" + else + buffer pass + fi + ;; + "not ok "* ) + let failures+=1 + buffer fail + ;; + "# "* ) + buffer log "${line:2}" + ;; + esac +done + +buffer summary diff --git a/git-secrets/test/bats/libexec/bats-preprocess b/git-secrets/test/bats/libexec/bats-preprocess new file mode 100755 index 00000000..04297ed0 --- /dev/null +++ b/git-secrets/test/bats/libexec/bats-preprocess @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e + +encode_name() { + local name="$1" + local result="test_" + + if [[ ! "$name" =~ [^[:alnum:]\ _-] ]]; then + name="${name//_/-5f}" + name="${name//-/-2d}" + name="${name// /_}" + result+="$name" + else + local length="${#name}" + local char i + + for ((i=0; i "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 0 ] +} + +@test "Scans all files when no file provided" { + setup_bad_repo + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Scans all files including history" { + setup_bad_repo + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans all files when no file provided with secret in history" { + setup_bad_repo_history + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Scans all files including history with secret in history" { + setup_bad_repo_history + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans history with secrets distributed among branches in history" { + cd $TEST_REPO + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + echo 'todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + git checkout -b testbranch + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + git checkout master + cd - + repo_run git-secrets --scan-history + [ $status -eq 1 ] +} + +@test "Scans recursively" { + setup_bad_repo + mkdir -p $TEST_REPO/foo/bar/baz + echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt + repo_run git-secrets --scan -r $TEST_REPO/foo + [ $status -eq 1 ] +} + +@test "Scans recursively only if -r is given" { + setup_bad_repo + mkdir -p $TEST_REPO/foo/bar/baz + echo '@todo more stuff' > $TEST_REPO/foo/bar/baz/data.txt + repo_run git-secrets --scan $TEST_REPO/foo + [ $status -eq 0 ] +} + +@test "Excludes allowed patterns from failures" { + git config --add secrets.patterns 'foo="baz{1,5}"' + git config --add secrets.allowed 'foo="bazzz"' + echo 'foo="bazzz" is ok because 3 "z"s' > "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 0 ] + echo 'This is NOT: ok foo="bazzzz"' > "$BATS_TMPDIR/test.txt" + repo_run git-secrets --scan "$BATS_TMPDIR/test.txt" + [ $status -eq 1 ] +} + +@test "Prohibited matches exits 1" { + file="$TEST_REPO/test.txt" + echo '@todo stuff' > $file + echo 'this is forbidden right?' >> $file + repo_run git-secrets --scan $file + [ $status -eq 1 ] + [ "${lines[0]}" == "$file:1:@todo stuff" ] + [ "${lines[1]}" == "$file:2:this is forbidden right?" ] +} + +@test "Only matches on word boundaries" { + file="$TEST_REPO/test.txt" + # Note that the following does not match as it is not a word. + echo 'mesa Jar Jar Binks' > $file + # The following do match because they are in word boundaries. + echo 'foo.me' >> $file + echo '"me"' >> $file + repo_run git-secrets --scan $file + [ $status -eq 1 ] + [ "${lines[0]}" == "$file:2:foo.me" ] + [ "${lines[1]}" == "$file:3:\"me\"" ] +} + +@test "Can scan from stdin using -" { + echo "foo" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - + echo "me" | "${BATS_TEST_DIRNAME}/../git-secrets" --scan - && exit 1 || true +} + +@test "installs hooks for repo" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + [ -f $TEST_REPO/.git/hooks/pre-commit ] + [ -f $TEST_REPO/.git/hooks/prepare-commit-msg ] + [ -f $TEST_REPO/.git/hooks/commit-msg ] +} + +@test "fails if hook exists and no -f" { + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --install $TEST_REPO + [ $status -eq 1 ] +} + +@test "Overwrites hooks if -f is given" { + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --install -f $TEST_REPO + [ $status -eq 0 ] +} + +@test "installs hooks for repo with Debian style directories" { + setup_bad_repo + mkdir $TEST_REPO/.git/hooks/pre-commit.d + mkdir $TEST_REPO/.git/hooks/prepare-commit-msg.d + mkdir $TEST_REPO/.git/hooks/commit-msg.d + run git-secrets --install $TEST_REPO + [ -f $TEST_REPO/.git/hooks/pre-commit.d/git-secrets ] + [ -f $TEST_REPO/.git/hooks/prepare-commit-msg.d/git-secrets ] + [ -f $TEST_REPO/.git/hooks/commit-msg.d/git-secrets ] +} + +@test "installs hooks to template directory" { + setup_bad_repo + run git-secrets --install $TEMPLATE_DIR + [ $status -eq 0 ] + run git init --template $TEMPLATE_DIR + [ $status -eq 0 ] + [ -f "${TEST_REPO}/.git/hooks/pre-commit" ] + [ -f "${TEST_REPO}/.git/hooks/prepare-commit-msg" ] + [ -f "${TEST_REPO}/.git/hooks/commit-msg" ] +} + +@test "Scans using keys from credentials file" { + echo 'aws_access_key_id = abc123' > $BATS_TMPDIR/test.ini + echo 'aws_secret_access_key=foobaz' >> $BATS_TMPDIR/test.ini + echo 'aws_access_key_id = "Bernard"' >> $BATS_TMPDIR/test.ini + echo 'aws_secret_access_key= "Laverne"' >> $BATS_TMPDIR/test.ini + echo 'aws_access_key_id= Hoagie+man' >> $BATS_TMPDIR/test.ini + cd $TEST_REPO + run git secrets --aws-provider $BATS_TMPDIR/test.ini + [ $status -eq 0 ] + echo "$output" | grep -F "foobaz" + echo "$output" | grep -F "abc123" + echo "$output" | grep -F "Bernard" + echo "$output" | grep -F "Laverne" + echo "$output" | grep -F 'Hoagie\+man' + run git secrets --add-provider -- git secrets --aws-provider $BATS_TMPDIR/test.ini + [ $status -eq 0 ] + echo '(foobaz) test' > $TEST_REPO/bad_file + echo "abc123 test" >> $TEST_REPO/bad_file + echo 'Bernard test' >> $TEST_REPO/bad_file + echo 'Laverne test' >> $TEST_REPO/bad_file + echo 'Hoagie+man test' >> $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 1 ] + echo "$output" | grep "foobaz" + echo "$output" | grep "abc123" + echo "$output" | grep "Bernard" + echo "$output" | grep "Laverne" + echo "$output" | grep -F 'Hoagie+man' +} + +@test "Lists secrets for a repo" { + repo_run git-secrets --list + [ $status -eq 0 ] + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' +} + +@test "Adds secrets to a repo and de-dedupes" { + repo_run git-secrets --add 'testing+123' + [ $status -eq 0 ] + repo_run git-secrets --add 'testing+123' + [ $status -eq 1 ] + repo_run git-secrets --add --literal 'testing+abc' + [ $status -eq 0 ] + repo_run git-secrets --add -l 'testing+abc' + [ $status -eq 1 ] + repo_run git-secrets --list + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' + echo "$output" | grep -F 'secrets.patterns testing+123' + echo "$output" | grep -F 'secrets.patterns testing\+abc' +} + +@test "Adds allowed patterns to a repo and de-dedupes" { + repo_run git-secrets --add -a 'testing+123' + [ $status -eq 0 ] + repo_run git-secrets --add --allowed 'testing+123' + [ $status -eq 1 ] + repo_run git-secrets --add -a -l 'testing+abc' + [ $status -eq 0 ] + repo_run git-secrets --add -a -l 'testing+abc' + [ $status -eq 1 ] + repo_run git-secrets --list + echo "$output" | grep -F 'secrets.patterns @todo' + echo "$output" | grep -F 'secrets.patterns forbidden|me' + echo "$output" | grep -F 'secrets.allowed testing+123' + echo "$output" | grep -F 'secrets.allowed testing\+abc' +} + +@test "Empty lines must be ignored in .gitallowed files" { + setup_bad_repo + echo '' >> $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Comment lines must be ignored in .gitallowed files" { + setup_bad_repo_with_hash + repo_run git-secrets --scan + [ $status -eq 1 ] + echo '#hash' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] + echo 'hash' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Scans all files and allowing none of the bad patterns in .gitallowed" { + setup_bad_repo + echo 'hello' > $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 1 ] +} + +@test "Scans all files and allowing all bad patterns in .gitallowed" { + setup_bad_repo + echo '@todo' > $TEST_REPO/.gitallowed + echo 'forbidden' >> $TEST_REPO/.gitallowed + echo 'me' >> $TEST_REPO/.gitallowed + repo_run git-secrets --scan + [ $status -eq 0 ] +} + +@test "Adds common AWS patterns" { + repo_run git config --unset-all secrets + repo_run git-secrets --register-aws + git config --local --get secrets.providers + repo_run git-secrets --list + echo "$output" | grep -F '(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}' + echo "$output" | grep "AKIAIOSFODNN7EXAMPLE" + echo "$output" | grep "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" +} + +@test "Adds providers" { + repo_run git-secrets --add-provider -- echo foo baz bar + [ $status -eq 0 ] + repo_run git-secrets --add-provider -- echo bam + [ $status -eq 0 ] + repo_run git-secrets --list + echo "$output" | grep -F 'echo foo baz bar' + echo "$output" | grep -F 'echo bam' + echo 'foo baz bar' > $TEST_REPO/bad_file + echo 'bam' >> $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 1 ] + echo "$output" | grep -F 'foo baz bar' + echo "$output" | grep -F 'bam' +} + +@test "Strips providers that return nothing" { + repo_run git-secrets --add-provider -- 'echo' + [ $status -eq 0 ] + repo_run git-secrets --add-provider -- 'echo 123' + [ $status -eq 0 ] + repo_run git-secrets --list + echo "$output" | grep -F 'echo 123' + echo 'foo' > $TEST_REPO/bad_file + repo_run git-secrets --scan $TEST_REPO/bad_file + [ $status -eq 0 ] +} + +@test "--recursive cannot be used with SCAN_*" { + repo_run git-secrets --scan -r --cached + [ $status -eq 1 ] + repo_run git-secrets --scan -r --no-index + [ $status -eq 1 ] + repo_run git-secrets --scan -r --untracked + [ $status -eq 1 ] +} + +@test "--recursive can be used with --scan" { + repo_run git-secrets --scan -r + [ $status -eq 0 ] +} + +@test "--recursive can't be used with --list" { + repo_run git-secrets --list -r + [ $status -eq 1 ] +} + +@test "-f can only be used with --install" { + repo_run git-secrets --scan -f + [ $status -eq 1 ] +} + +@test "-a can only be used with --add" { + repo_run git-secrets --scan -a + [ $status -eq 1 ] +} + +@test "-l can only be used with --add" { + repo_run git-secrets --scan -l + [ $status -eq 1 ] +} + +@test "--cached can only be used with --scan" { + repo_run git-secrets --list --cached + [ $status -eq 1 ] +} + +@test "--no-index can only be used with --scan" { + repo_run git-secrets --list --no-index + [ $status -eq 1 ] +} + +@test "--untracked can only be used with --scan" { + repo_run git-secrets --list --untracked + [ $status -eq 1 ] +} diff --git a/git-secrets/test/pre-commit.bats b/git-secrets/test/pre-commit.bats new file mode 100644 index 00000000..5ace267c --- /dev/null +++ b/git-secrets/test/pre-commit.bats @@ -0,0 +1,62 @@ +#!/usr/bin/env bats +load test_helper + +@test "Rejects commits with prohibited patterns in changeset" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] + [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] + [ "${lines[2]}" == "failure2.txt:1:me" ] +} + +@test "Rejects commits with prohibited patterns in changeset with filename that contain spaces" { + setup_bad_repo_with_spaces + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "da ta.txt:1:@todo more stuff" ] +} + +@test "Scans staged files" { + cd $TEST_REPO + repo_run git-secrets --install $TEST_REPO + echo '@todo more stuff' > $TEST_REPO/data.txt + echo 'hi there' > $TEST_REPO/ok.txt + git add -A + echo 'fixed the working directory, but not staged' > $TEST_REPO/data.txt + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] +} + +@test "Allows commits that do not match prohibited patterns" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + run git commit -m 'This is fine' + [ $status -eq 0 ] + # Ensure deleted files are filtered out of the grep + rm $TEST_REPO/data.txt + echo 'aaa' > $TEST_REPO/data_2.txt + run git add -A + run git commit -m 'This is also fine' + [ $status -eq 0 ] +} + +@test "Rejects commits with prohibited patterns in changeset when AWS provider is enabled" { + setup_bad_repo + repo_run git-secrets --install $TEST_REPO + repo_run git-secrets --register-aws $TEST_REPO + cd $TEST_REPO + run git commit -m 'Contents are bad not the message' + [ $status -eq 1 ] + echo "${lines}" | grep -vq 'git secrets --aws-provider: command not found' + + [ "${lines[0]}" == "data.txt:1:@todo more stuff" ] + [ "${lines[1]}" == "failure1.txt:1:another line... forbidden" ] + [ "${lines[2]}" == "failure2.txt:1:me" ] +} diff --git a/git-secrets/test/prepare-commit-msg.bats b/git-secrets/test/prepare-commit-msg.bats new file mode 100644 index 00000000..a211c131 --- /dev/null +++ b/git-secrets/test/prepare-commit-msg.bats @@ -0,0 +1,33 @@ +#!/usr/bin/env bats +load test_helper + +@test "Rejects merges with prohibited patterns in history" { + setup_good_repo + repo_run git-secrets --install $TEST_REPO + cd $TEST_REPO + git commit -m 'OK' + git checkout -b feature + echo '@todo' > data.txt + git add -A + git commit -m 'Bad commit' --no-verify + echo 'Fixing!' > data.txt + git add -A + git commit -m 'Fixing commit' + git checkout master + run git merge --no-ff feature + [ $status -eq 1 ] +} + +@test "Allows merges that do not match prohibited patterns" { + setup_good_repo + cd $TEST_REPO + repo_run git-secrets --install + git commit -m 'OK' + git checkout -b feature + echo 'Not bad' > data.txt + git add -A + git commit -m 'Good commit' + git checkout master + run git merge --no-ff feature + [ $status -eq 0 ] +} diff --git a/git-secrets/test/test_helper.bash b/git-secrets/test/test_helper.bash new file mode 100644 index 00000000..9133e516 --- /dev/null +++ b/git-secrets/test/test_helper.bash @@ -0,0 +1,94 @@ +#!/bin/bash +export TEST_REPO="$BATS_TMPDIR/test-repo" +export TEMP_HOME="$BATS_TMPDIR/home" +export TEMPLATE_DIR="${BATS_TMPDIR}/template" +INITIAL_PATH="${PATH}" +INITIAL_HOME=${HOME} + +setup() { + setup_repo + [ -d "${TEMPLATE_DIR}" ] && rm -rf "${TEMPLATE_DIR}" + [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" + mkdir -p $TEMP_HOME + export HOME=$TEMP_HOME + export PATH="${BATS_TEST_DIRNAME}/..:${INITIAL_PATH}" + cd $TEST_REPO +} + +teardown() { + delete_repo + export PATH="${INITIAL_PATH}" + export HOME="${INITIAL_HOME}" + [ -d "${TEMP_HOME}" ] && rm -rf "${TEMP_HOME}" +} + +delete_repo() { + [ -d $TEST_REPO ] && rm -rf $TEST_REPO || true +} + +setup_repo() { + delete_repo + mkdir -p $TEST_REPO + cd $TEST_REPO + git init + git config --local --add secrets.patterns '@todo' + git config --local --add secrets.patterns 'forbidden|me' + git config --local --add secrets.patterns '#hash' + git config --local user.email "you@example.com" + git config --local user.name "Your Name" + cd - +} + +repo_run() { + cmd="$1" + shift + cd "${TEST_REPO}" + run "${BATS_TEST_DIRNAME}/../${cmd}" $@ + cd - +} + +# Creates a repo that should fail +setup_bad_repo() { + cd $TEST_REPO + echo '@todo more stuff' > $TEST_REPO/data.txt + echo 'hi there' > $TEST_REPO/ok.txt + echo 'another line... forbidden' > $TEST_REPO/failure1.txt + echo 'me' > $TEST_REPO/failure2.txt + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_with_spaces() { + cd $TEST_REPO + echo '@todo more stuff' > "$TEST_REPO/da ta.txt" + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_with_hash() { + cd $TEST_REPO + echo '#hash' > "$TEST_REPO/data.txt" + git add -A + cd - +} + +# Creates a repo that should fail +setup_bad_repo_history() { + cd $TEST_REPO + echo '@todo' > $TEST_REPO/history_failure.txt + git add -A + git commit -m "Testing history" + echo 'todo' > $TEST_REPO/history_failure.txt + git add -A + cd - +} + +# Creates a repo that does not fail +setup_good_repo() { + cd $TEST_REPO + echo 'hello!' > $TEST_REPO/data.txt + git add -A + cd - +} diff --git a/locals.tf b/locals.tf index 4678b5eb..1a88a1b7 100644 --- a/locals.tf +++ b/locals.tf @@ -23,4 +23,4 @@ locals { aft_tf_ddb_table_ssm_path = "/aft/config/oss-backend/table-id" aft_tf_version_ssm_path = "/aft/config/terraform/version" } -} \ No newline at end of file +} diff --git a/main.tf b/main.tf index 0ddb6d62..1f74fb95 100644 --- a/main.tf +++ b/main.tf @@ -222,4 +222,4 @@ module "aft_ssm_parameters" { account_provisioning_customizations_repo_branch = var.account_provisioning_customizations_repo_branch maximum_concurrent_customizations = var.maximum_concurrent_customizations github_enterprise_url = var.github_enterprise_url -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/data.tf b/modules/aft-account-provisioning-framework/data.tf index 678e9234..eb260877 100644 --- a/modules/aft-account-provisioning-framework/data.tf +++ b/modules/aft-account-provisioning-framework/data.tf @@ -6,4 +6,4 @@ data "aws_iam_policy" "AWSLambdaBasicExecutionRole" { data "aws_iam_policy" "AWSLambdaVPCAccessExecutionRole" { name = "AWSLambdaVPCAccessExecutionRole" -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/iam.tf b/modules/aft-account-provisioning-framework/iam.tf index b5d18557..ed54dd8c 100644 --- a/modules/aft-account-provisioning-framework/iam.tf +++ b/modules/aft-account-provisioning-framework/iam.tf @@ -135,4 +135,4 @@ resource "aws_iam_role_policy" "aft_states" { data_aws_region_aft-management_name = data.aws_region.aft_management.name data_aws_caller_identity_aft-management_account_id = data.aws_caller_identity.aft_management.account_id }) -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/iam/role-policies/iam-aft-states.tpl b/modules/aft-account-provisioning-framework/iam/role-policies/iam-aft-states.tpl index 39da4aec..2e8ed75c 100644 --- a/modules/aft-account-provisioning-framework/iam/role-policies/iam-aft-states.tpl +++ b/modules/aft-account-provisioning-framework/iam/role-policies/iam-aft-states.tpl @@ -54,4 +54,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/iam/trust-policies/lambda.tpl b/modules/aft-account-provisioning-framework/iam/trust-policies/lambda.tpl index e1a853ce..fb84ae9d 100644 --- a/modules/aft-account-provisioning-framework/iam/trust-policies/lambda.tpl +++ b/modules/aft-account-provisioning-framework/iam/trust-policies/lambda.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/iam/trust-policies/states.tpl b/modules/aft-account-provisioning-framework/iam/trust-policies/states.tpl index 195191c8..9217421a 100644 --- a/modules/aft-account-provisioning-framework/iam/trust-policies/states.tpl +++ b/modules/aft-account-provisioning-framework/iam/trust-policies/states.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/lambda.tf b/modules/aft-account-provisioning-framework/lambda.tf index 001c9f6c..e8c5187e 100644 --- a/modules/aft-account-provisioning-framework/lambda.tf +++ b/modules/aft-account-provisioning-framework/lambda.tf @@ -148,4 +148,34 @@ resource "aws_lambda_function" "persist_metadata" { resource "aws_cloudwatch_log_group" "persist_metadata" { name = "/aws/lambda/${aws_lambda_function.persist_metadata.function_name}" retention_in_days = var.cloudwatch_log_group_retention -} \ No newline at end of file +} + +### Account Metadata SSM Function + +data "archive_file" "account_metadata_ssm" { + type = "zip" + source_dir = "${path.module}/lambda/aft-account-provisioning-framework-account-metadata-ssm/" + output_path = "${path.module}/account_metadata_ssm.zip" +} + +resource "aws_lambda_function" "account_metadata_ssm" { + filename = data.archive_file.account_metadata_ssm.output_path + function_name = "aft-account-provisioning-framework-account-metadata-ssm" + description = "AFT account provisioning framework - account_metadata_ssm" + role = aws_iam_role.aft_lambda_aft_account_provisioning_framework_persist_metadata.arn + handler = "aft_account_provisioning_framework_account_metadata_ssm.lambda_handler" + source_code_hash = data.archive_file.account_metadata_ssm.output_base64sha256 + runtime = "python3.8" + timeout = 300 + layers = [var.aft_common_layer_arn] + + vpc_config { + subnet_ids = var.aft_vpc_private_subnets + security_group_ids = var.aft_vpc_default_sg + } +} + +resource "aws_cloudwatch_log_group" "account_metadata_ssm" { + name = "/aws/lambda/${aws_lambda_function.account_metadata_ssm.function_name}" + retention_in_days = var.cloudwatch_log_group_retention +} diff --git a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-account-metadata-ssm/aft_account_provisioning_framework_account_metadata_ssm.py b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-account-metadata-ssm/aft_account_provisioning_framework_account_metadata_ssm.py new file mode 100644 index 00000000..b7963719 --- /dev/null +++ b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-account-metadata-ssm/aft_account_provisioning_framework_account_metadata_ssm.py @@ -0,0 +1,137 @@ +import inspect +import json +from typing import TYPE_CHECKING, Any, Dict, List, Sequence, Union + +import aft_common.aft_utils as utils +import boto3 +from boto3.session import Session + +if TYPE_CHECKING: + from mypy_boto3_ssm import SSMClient + from mypy_boto3_sts import STSClient +else: + SSMClient = object + STSClient = object + + +AFT_EXEC_ROLE = "AWSAFTExecution" + +SSM_PARAMETER_PATH = "/aft/account-request/custom-fields/" + +logger = utils.get_logger() + + +def get_ssm_parameters_names_by_path(session: Session, path: str) -> List[str]: + + client = session.client("ssm") + response = client.get_parameters_by_path(Path=path, Recursive=True) + logger.debug(response) + + parameter_names = [] + for p in response["Parameters"]: + parameter_names.append(p["Name"]) + + return parameter_names + + +def delete_ssm_parameters(session: Session, parameters: Sequence[str]) -> None: + + if len(parameters) > 0: + client = session.client("ssm") + response = client.delete_parameters(Names=parameters) + logger.info(response) + + +def create_ssm_parameters(session: Session, parameters: Dict[str, str]) -> None: + + client = session.client("ssm") + + for key, value in parameters.items(): + response = client.put_parameter( + Name=SSM_PARAMETER_PATH + key, Value=value, Type="String", Overwrite=True + ) + logger.info(response) + + +def lambda_handler(event: Dict[str, Any], context: Union[Dict[str, Any], None]) -> None: + try: + account_request = event["payload"]["account_request"] + custom_fields = json.loads(account_request.get("custom_fields", "{}")) + target_account_id = event["payload"]["account_info"]["account"]["id"] + + local_session = boto3.session.Session() + + aft_session = utils.get_aft_admin_role_session(local_session) + target_account_role_arn = utils.build_role_arn( + aft_session, AFT_EXEC_ROLE, target_account_id + ) + + # Create the custom field parameters in the AFT home region + target_region = aft_session.region_name + + aft_ssm_session_policy = { + "Version": "2012-10-17", + "Statement": [ + { + "Action": [ + "ssm:GetParametersByPath", + "ssm:PutParameter", + "ssm:DeleteParameters", + ], + "Effect": "Allow", + "Resource": f"arn:aws:ssm:{target_region}:{target_account_id}:parameter{SSM_PARAMETER_PATH}*", + } + ], + } + + target_account_creds = utils.get_assume_role_credentials( + session=aft_session, + role_arn=target_account_role_arn, + session_name="aft_ssm_metadata", + session_policy=json.dumps(aft_ssm_session_policy), + ) + target_account_session = utils.get_boto_session(target_account_creds) + + params = get_ssm_parameters_names_by_path( + target_account_session, SSM_PARAMETER_PATH + ) + + existing_keys = set(params) + new_keys = set(custom_fields.keys()) + + # Delete SSM parameters which do not exist in new custom fields + params_to_remove = list(existing_keys.difference(new_keys)) + logger.info(message=f"Deleting SSM params: {params_to_remove}") + delete_ssm_parameters(target_account_session, params_to_remove) + + # Update / Add SSM parameters for custom fields provided + logger.info(message=f"Adding/Updating SSM params: {custom_fields}") + create_ssm_parameters(target_account_session, custom_fields) + + except Exception as e: + message = { + "FILE": __file__.split("/")[-1], + "METHOD": inspect.stack()[0][3], + "EXCEPTION": str(e), + } + logger.exception(message) + raise + + +if __name__ == "__main__": + import json + import sys + from optparse import OptionParser + + logger.info("Local Execution") + parser = OptionParser() + parser.add_option( + "-f", "--event-file", dest="event_file", help="Event file to be processed" + ) + (options, args) = parser.parse_args(sys.argv) + if options.event_file is not None: + with open(options.event_file) as json_data: + event = json.load(json_data) + lambda_handler(event, None) + else: + lambda_handler({}, None) diff --git a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/aft_account_provisioning_framework_create_role.py b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/aft_account_provisioning_framework_create_role.py index f910f241..90c7c1db 100644 --- a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/aft_account_provisioning_framework_create_role.py +++ b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/aft_account_provisioning_framework_create_role.py @@ -1,12 +1,20 @@ import inspect import json import os -from typing import Any, Dict, Union +from typing import TYPE_CHECKING, Any, Dict, Union import aft_common.aft_utils as utils import boto3 from boto3.session import Session +if TYPE_CHECKING: + from mypy_boto3_iam import IAMClient + from mypy_boto3_iam.type_defs import CreateRoleResponseTypeDef +else: + IAMClient = object + CreateRoleResponseTypeDef = object + + logger = utils.get_logger() @@ -70,16 +78,12 @@ def create_aft_execution_role( exec_iam_client = ct_execution_session.client("iam") try: - return_value: str role = exec_iam_client.get_role(RoleName=role_name.split("/")[-1]) logger.info("Role Exists. Exiting") - return_value = role["Role"]["Arn"] - return return_value + return role["Role"]["Arn"] except exec_iam_client.exceptions.NoSuchEntityException: - logger.info("Role not found in account.") - role = create_role_in_account(session, ct_execution_session, role_name) - return_value = role - return return_value + logger.info("Role not found in account. Creating...") + return create_role_in_account(session, ct_execution_session, role_name) def create_role_in_account( @@ -97,9 +101,9 @@ def create_role_in_account( template = template.replace("{AftManagementAccount}", aft_management_account) assume_role_policy_document = json.loads(template) - exec_client = ct_execution_session.client("iam") + exec_client: IAMClient = ct_execution_session.client("iam") logger.info("Creating Role") - response = exec_client.create_role( + response: CreateRoleResponseTypeDef = exec_client.create_role( RoleName=role_name.split("/")[-1], AssumeRolePolicyDocument=json.dumps(assume_role_policy_document), Description="AFT Execution Role", @@ -111,15 +115,13 @@ def create_role_in_account( role = response["Role"]["Arn"] logger.info(response) logger.info("Attaching Role Policy") - response = exec_client.attach_role_policy( + exec_client.attach_role_policy( RoleName=role_name.split("/")[-1], PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess", ) - logger.info(response) logger.info("Returning role") logger.info(role) - return_value: str = role - return return_value + return role def lambda_handler(event: Dict[str, Any], context: Union[Dict[str, Any], None]) -> str: diff --git a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/iam/trust-policies/aftmanagement.tpl b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/iam/trust-policies/aftmanagement.tpl index 0339310c..14789505 100644 --- a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/iam/trust-policies/aftmanagement.tpl +++ b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-create-role/iam/trust-policies/aftmanagement.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-validate-request/schema/request_schema.json b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-validate-request/schema/request_schema.json index 6b3d440e..103cab1d 100644 --- a/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-validate-request/schema/request_schema.json +++ b/modules/aft-account-provisioning-framework/lambda/aft-account-provisioning-framework-validate-request/schema/request_schema.json @@ -72,4 +72,4 @@ } }, "required": [ "account_request", "control_tower_event" ] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/locals.tf b/modules/aft-account-provisioning-framework/locals.tf index a94d50e8..59c5315e 100644 --- a/modules/aft-account-provisioning-framework/locals.tf +++ b/modules/aft-account-provisioning-framework/locals.tf @@ -1,3 +1,3 @@ locals { lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/states.tf b/modules/aft-account-provisioning-framework/states.tf index e7519c6c..75bd93bd 100644 --- a/modules/aft-account-provisioning-framework/states.tf +++ b/modules/aft-account-provisioning-framework/states.tf @@ -6,6 +6,7 @@ locals { create_role_function_name = aws_lambda_function.create_role.function_name tag_account_function_name = aws_lambda_function.tag_account.function_name persist_metadata_function_name = aws_lambda_function.persist_metadata.function_name + account_metadata_ssm_function_name = aws_lambda_function.account_metadata_ssm.function_name aft_notification_arn = var.aft_sns_topic_arn aft_failure_notification_arn = var.aft_failure_sns_topic_arn aft_account_provisioning_customizations_state_machine_arn = "arn:aws:states:${data.aws_region.aft_management.name}:${data.aws_caller_identity.aft_management.account_id}:stateMachine:${var.aft_account_provisioning_customizations_sfn_name}" @@ -18,4 +19,4 @@ resource "aws_sfn_state_machine" "aft_account_provisioning_framework_sfn" { name = var.aft_account_provisioning_framework_sfn_name role_arn = aws_iam_role.aft_states.arn definition = templatefile(local.state_machine_source, local.replacements_map) -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/states/aft_account_provisioning_framework.asl.json b/modules/aft-account-provisioning-framework/states/aft_account_provisioning_framework.asl.json index ca3c4ef2..551b31d3 100644 --- a/modules/aft-account-provisioning-framework/states/aft_account_provisioning_framework.asl.json +++ b/modules/aft-account-provisioning-framework/states/aft_account_provisioning_framework.asl.json @@ -94,7 +94,7 @@ ] }, "aft_account_provisioning_framework_tag_account": { - "Next": "aft_account_provisioning_framework_aft_features", + "Next": "aft_account_provisioning_framework_account_metadata_ssm", "Type": "Task", "Resource": "arn:aws:states:::lambda:invoke", "ResultPath": "$.account_tags", @@ -115,6 +115,28 @@ } ] }, + "aft_account_provisioning_framework_account_metadata_ssm": { + "Next": "aft_account_provisioning_framework_aft_features", + "Type": "Task", + "Resource": "arn:aws:states:::lambda:invoke", + "ResultPath": "$.account_metadata_ssm", + "ResultSelector": {"StatusCode.$":"$.StatusCode"}, + "Parameters": { + "FunctionName": "${account_metadata_ssm_function_name}", + "Payload": { + "job_name.$": "$$.Execution.Name", + "payload.$": "$", + "action": "account_metadata_ssm", + "account_info.$": "$.account_info" + } + }, + "Catch": [ + { + "ErrorEquals": ["States.ALL"], + "Next": "aft_account_provisioning_framework_notify_error" + } + ] + }, "aft_account_provisioning_framework_aft_features": { "Next": "aft_account_provisioning_customizations", "Type": "Task", diff --git a/modules/aft-account-provisioning-framework/variables.tf b/modules/aft-account-provisioning-framework/variables.tf index 925b9073..8d906fd7 100644 --- a/modules/aft-account-provisioning-framework/variables.tf +++ b/modules/aft-account-provisioning-framework/variables.tf @@ -40,4 +40,4 @@ variable "aft_vpc_private_subnets" { variable "aft_vpc_default_sg" { type = list(string) -} \ No newline at end of file +} diff --git a/modules/aft-account-provisioning-framework/versions.tf b/modules/aft-account-provisioning-framework/versions.tf index 18d8cbb8..60b8c501 100755 --- a/modules/aft-account-provisioning-framework/versions.tf +++ b/modules/aft-account-provisioning-framework/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/backup.tf b/modules/aft-account-request-framework/backup.tf index 71a95ea3..aaa07a3d 100644 --- a/modules/aft-account-request-framework/backup.tf +++ b/modules/aft-account-request-framework/backup.tf @@ -22,4 +22,4 @@ resource "aws_backup_selection" "aft_controltower_backup_selection" { aws_dynamodb_table.aft_request_audit.arn, aws_dynamodb_table.aft_controltower_events.arn ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/data.tf b/modules/aft-account-request-framework/data.tf index 45d15442..e5ba84e3 100644 --- a/modules/aft-account-request-framework/data.tf +++ b/modules/aft-account-request-framework/data.tf @@ -300,4 +300,4 @@ data "aws_subnet_ids" "sts" { name = "availability-zone" values = data.aws_vpc_endpoint_service.sts.availability_zones } -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/eventbridge.tf b/modules/aft-account-request-framework/eventbridge.tf index c6a62574..b77bb368 100644 --- a/modules/aft-account-request-framework/eventbridge.tf +++ b/modules/aft-account-request-framework/eventbridge.tf @@ -68,4 +68,4 @@ resource "aws_cloudwatch_event_rule" "aft_account_request_processor" { resource "aws_cloudwatch_event_target" "aft_account_request_processor" { arn = aws_lambda_function.aft_account_request_processor.arn rule = aws_cloudwatch_event_rule.aft_account_request_processor.id -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam.tf b/modules/aft-account-request-framework/iam.tf index 879aa77d..b19ca2e8 100644 --- a/modules/aft-account-request-framework/iam.tf +++ b/modules/aft-account-request-framework/iam.tf @@ -167,4 +167,4 @@ resource "aws_iam_role" "aft_aws_backup" { resource "aws_iam_role_policy_attachment" "aft_aws_backup_service_role" { role = aws_iam_role.aft_aws_backup.name policy_arn = "arn:aws:iam::aws:policy/service-role/AWSBackupServiceRolePolicyForBackup" -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aft-code-commit-power-user.tpl b/modules/aft-account-request-framework/iam/role-policies/aft-code-commit-power-user.tpl index 9341292b..16927bfe 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aft-code-commit-power-user.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aft-code-commit-power-user.tpl @@ -199,4 +199,4 @@ "Resource": "arn:aws:codestar-connections:*:*:connection/*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aft-get-ct-role.tpl b/modules/aft-account-request-framework/iam/role-policies/aft-get-ct-role.tpl index 13ddcfcd..85957d04 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aft-get-ct-role.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aft-get-ct-role.tpl @@ -7,4 +7,4 @@ "Resource": "arn:aws:iam::${data_aws_caller_identity_aft-management_account_id}:role/AWSControlTowerExecution" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aft-states.tpl b/modules/aft-account-request-framework/iam/role-policies/aft-states.tpl index 00a13a5f..521a7dd2 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aft-states.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aft-states.tpl @@ -16,4 +16,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume.tpl b/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume.tpl index 399038b4..5a152a29 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume.tpl @@ -22,4 +22,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume_ct_admin.tpl b/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume_ct_admin.tpl index ffabaa60..ec3bb04d 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume_ct_admin.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aft-sts-assume_ct_admin.tpl @@ -7,4 +7,4 @@ "Resource": "arn:aws:iam::*:role/AWSControlTowerExecution" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aftService-gitlab-permissions.tpl b/modules/aft-account-request-framework/iam/role-policies/aftService-gitlab-permissions.tpl index 45e1677c..52eac675 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aftService-gitlab-permissions.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aftService-gitlab-permissions.tpl @@ -14,4 +14,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/aftService-ssm.tpl b/modules/aft-account-request-framework/iam/role-policies/aftService-ssm.tpl index 27dea908..e865fa3a 100644 --- a/modules/aft-account-request-framework/iam/role-policies/aftService-ssm.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/aftService-ssm.tpl @@ -9,4 +9,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/role-policies/events-control-tower-events.tpl b/modules/aft-account-request-framework/iam/role-policies/events-control-tower-events.tpl index 957a9dca..09d2b4d1 100644 --- a/modules/aft-account-request-framework/iam/role-policies/events-control-tower-events.tpl +++ b/modules/aft-account-request-framework/iam/role-policies/events-control-tower-events.tpl @@ -11,4 +11,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/trust-policies/backup.tpl b/modules/aft-account-request-framework/iam/trust-policies/backup.tpl index 26334547..e8cc8575 100644 --- a/modules/aft-account-request-framework/iam/trust-policies/backup.tpl +++ b/modules/aft-account-request-framework/iam/trust-policies/backup.tpl @@ -9,4 +9,4 @@ "Action": ["sts:AssumeRole"] } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/trust-policies/events.tpl b/modules/aft-account-request-framework/iam/trust-policies/events.tpl index b875d5ad..68c4a60f 100644 --- a/modules/aft-account-request-framework/iam/trust-policies/events.tpl +++ b/modules/aft-account-request-framework/iam/trust-policies/events.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/trust-policies/lambda.tpl b/modules/aft-account-request-framework/iam/trust-policies/lambda.tpl index e1a853ce..fb84ae9d 100644 --- a/modules/aft-account-request-framework/iam/trust-policies/lambda.tpl +++ b/modules/aft-account-request-framework/iam/trust-policies/lambda.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/iam/trust-policies/states.tpl b/modules/aft-account-request-framework/iam/trust-policies/states.tpl index 195191c8..9217421a 100644 --- a/modules/aft-account-request-framework/iam/trust-policies/states.tpl +++ b/modules/aft-account-request-framework/iam/trust-policies/states.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/kms.tf b/modules/aft-account-request-framework/kms.tf index 0aea54c9..d992f42f 100644 --- a/modules/aft-account-request-framework/kms.tf +++ b/modules/aft-account-request-framework/kms.tf @@ -5,4 +5,4 @@ resource "aws_kms_key" "aft" { resource "aws_kms_alias" "aft" { name = "alias/aft" target_key_id = aws_kms_key.aft.key_id -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/lambda.tf b/modules/aft-account-request-framework/lambda.tf index 33878fe5..e489b982 100644 --- a/modules/aft-account-request-framework/lambda.tf +++ b/modules/aft-account-request-framework/lambda.tf @@ -204,4 +204,4 @@ resource "aws_lambda_permission" "aft_invoke_aft_account_provisioning_framework" resource "aws_cloudwatch_log_group" "aft_invoke_aft_account_provisioning_framework" { name = "/aws/lambda/${aws_lambda_function.aft_invoke_aft_account_provisioning_framework.function_name}" retention_in_days = var.cloudwatch_log_group_retention -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/locals.tf b/modules/aft-account-request-framework/locals.tf index a94d50e8..59c5315e 100644 --- a/modules/aft-account-request-framework/locals.tf +++ b/modules/aft-account-request-framework/locals.tf @@ -1,3 +1,3 @@ locals { lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/outputs.tf b/modules/aft-account-request-framework/outputs.tf index d320b276..ec9e857f 100644 --- a/modules/aft-account-request-framework/outputs.tf +++ b/modules/aft-account-request-framework/outputs.tf @@ -87,4 +87,4 @@ output "aft_vpc_private_subnets" { output "aft_vpc_default_sg" { value = tolist([aws_security_group.aft_vpc_default_sg.id]) -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/sns.tf b/modules/aft-account-request-framework/sns.tf index 949e3e7f..8f3e6bfe 100644 --- a/modules/aft-account-request-framework/sns.tf +++ b/modules/aft-account-request-framework/sns.tf @@ -6,4 +6,4 @@ resource "aws_sns_topic" "aft_notifications" { resource "aws_sns_topic" "aft_failure_notifications" { name = "aft-failure-notifications" kms_master_key_id = "alias/aws/sns" -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/sqs.tf b/modules/aft-account-request-framework/sqs.tf index eba1bd2e..85f02d6c 100644 --- a/modules/aft-account-request-framework/sqs.tf +++ b/modules/aft-account-request-framework/sqs.tf @@ -14,4 +14,4 @@ resource "aws_sqs_queue" "aft_account_request_dlq" { fifo_queue = true kms_master_key_id = aws_kms_alias.aft.name kms_data_key_reuse_period_seconds = 300 -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/variables.tf b/modules/aft-account-request-framework/variables.tf index c64dfa7c..1067c40d 100644 --- a/modules/aft-account-request-framework/variables.tf +++ b/modules/aft-account-request-framework/variables.tf @@ -36,4 +36,4 @@ variable "aft_vpc_public_subnet_02_cidr" { variable "aft_vpc_endpoints" { type = bool -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/versions.tf b/modules/aft-account-request-framework/versions.tf index 5201ccc8..4df2681d 100755 --- a/modules/aft-account-request-framework/versions.tf +++ b/modules/aft-account-request-framework/versions.tf @@ -8,4 +8,4 @@ terraform { configuration_aliases = [aws.ct_management] } } -} \ No newline at end of file +} diff --git a/modules/aft-account-request-framework/vpc.tf b/modules/aft-account-request-framework/vpc.tf index 5944e8a6..65fc7b5c 100644 --- a/modules/aft-account-request-framework/vpc.tf +++ b/modules/aft-account-request-framework/vpc.tf @@ -413,4 +413,3 @@ resource "aws_vpc_endpoint" "sts" { private_dns_enabled = true } - diff --git a/modules/aft-code-repositories/codebuild.tf b/modules/aft-code-repositories/codebuild.tf index b9723af1..dc7041fb 100644 --- a/modules/aft-code-repositories/codebuild.tf +++ b/modules/aft-code-repositories/codebuild.tf @@ -98,4 +98,4 @@ resource "aws_cloudwatch_log_group" "account_request" { resource "aws_cloudwatch_log_group" "account_provisioning_customizations" { name = "/aws/codebuild/ct-aft-account-provisioning-customizations" retention_in_days = var.log_group_retention -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/codecommit.tf b/modules/aft-code-repositories/codecommit.tf index b8aaeaf4..1c527ad4 100644 --- a/modules/aft-code-repositories/codecommit.tf +++ b/modules/aft-code-repositories/codecommit.tf @@ -24,4 +24,4 @@ resource "aws_codecommit_repository" "account_provisioning_customizations" { repository_name = var.account_provisioning_customizations_repo_name description = "This repo holds the Account Provisioning Customizations Step Function Terraform Project" default_branch = var.account_provisioning_customizations_repo_branch -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/codepipeline.tf b/modules/aft-code-repositories/codepipeline.tf index 8e1f0a8b..e7fb91b2 100644 --- a/modules/aft-code-repositories/codepipeline.tf +++ b/modules/aft-code-repositories/codepipeline.tf @@ -339,4 +339,3 @@ resource "aws_cloudwatch_event_target" "account_provisioning_customizations" { arn = aws_codepipeline.codecommit_account_provisioning_customizations[0].arn role_arn = aws_iam_role.cloudwatch_events_codepipeline_role[0].arn } - diff --git a/modules/aft-code-repositories/codestar.tf b/modules/aft-code-repositories/codestar.tf index aff415ad..28ece6e8 100644 --- a/modules/aft-code-repositories/codestar.tf +++ b/modules/aft-code-repositories/codestar.tf @@ -28,4 +28,3 @@ resource "aws_codestarconnections_host" "githubenterprise" { vpc_id = var.vpc_id } } - diff --git a/modules/aft-code-repositories/data.tf b/modules/aft-code-repositories/data.tf index cfd929b6..eb58f218 100644 --- a/modules/aft-code-repositories/data.tf +++ b/modules/aft-code-repositories/data.tf @@ -1,4 +1,3 @@ data "aws_region" "current" {} data "aws_caller_identity" "current" {} - diff --git a/modules/aft-code-repositories/iam.tf b/modules/aft-code-repositories/iam.tf index b8f101a4..78f6d3a5 100644 --- a/modules/aft-code-repositories/iam.tf +++ b/modules/aft-code-repositories/iam.tf @@ -119,4 +119,4 @@ resource "aws_iam_role_policy" "cloudwatch_events_codepipeline_role" { account_request_pipeline_name = aws_codepipeline.codecommit_account_request[0].name provisioning_customizations_pipeline_name = aws_codepipeline.codecommit_account_provisioning_customizations[0].name }) -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl b/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl index 806c7334..d4cd8042 100644 --- a/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl +++ b/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl @@ -35,4 +35,4 @@ "Resource": "arn:aws:kms:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:key/${aws_s3_bucket_aft_terraform_oss_kms_key_id}" } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_policy.tpl b/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_policy.tpl index eb1d387d..1668da7e 100644 --- a/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_policy.tpl +++ b/modules/aft-code-repositories/iam/role-policies/ct_aft_codebuild_policy.tpl @@ -97,4 +97,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/role-policies/ct_aft_cwe_policy.tpl b/modules/aft-code-repositories/iam/role-policies/ct_aft_cwe_policy.tpl index c9994b80..776eb84d 100644 --- a/modules/aft-code-repositories/iam/role-policies/ct_aft_cwe_policy.tpl +++ b/modules/aft-code-repositories/iam/role-policies/ct_aft_cwe_policy.tpl @@ -12,4 +12,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/trust-policies/codebuild.tpl b/modules/aft-code-repositories/iam/trust-policies/codebuild.tpl index 23e96ea1..3af7c641 100644 --- a/modules/aft-code-repositories/iam/trust-policies/codebuild.tpl +++ b/modules/aft-code-repositories/iam/trust-policies/codebuild.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/trust-policies/codepipeline.tpl b/modules/aft-code-repositories/iam/trust-policies/codepipeline.tpl index be32a2ea..9be3f72b 100644 --- a/modules/aft-code-repositories/iam/trust-policies/codepipeline.tpl +++ b/modules/aft-code-repositories/iam/trust-policies/codepipeline.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/iam/trust-policies/events.tpl b/modules/aft-code-repositories/iam/trust-policies/events.tpl index b875d5ad..68c4a60f 100644 --- a/modules/aft-code-repositories/iam/trust-policies/events.tpl +++ b/modules/aft-code-repositories/iam/trust-policies/events.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/locals.tf b/modules/aft-code-repositories/locals.tf index d6bbe849..db85b480 100644 --- a/modules/aft-code-repositories/locals.tf +++ b/modules/aft-code-repositories/locals.tf @@ -11,4 +11,4 @@ locals { githubenterprise = lower(var.vcs_provider) == "githubenterprise" ? aws_codestarconnections_connection.githubenterprise[0].arn : "" codecommit = "null" } -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/outputs.tf b/modules/aft-code-repositories/outputs.tf index 03330e87..81d37e56 100644 --- a/modules/aft-code-repositories/outputs.tf +++ b/modules/aft-code-repositories/outputs.tf @@ -1,3 +1,3 @@ output "codestar_connection_arn" { value = lookup(local.connection_arn, var.vcs_provider) -} \ No newline at end of file +} diff --git a/modules/aft-code-repositories/versions.tf b/modules/aft-code-repositories/versions.tf index 18d8cbb8..60b8c501 100755 --- a/modules/aft-code-repositories/versions.tf +++ b/modules/aft-code-repositories/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/modules/aft-customizations/data.tf b/modules/aft-customizations/data.tf index 0ce9a447..34c91b7c 100644 --- a/modules/aft-customizations/data.tf +++ b/modules/aft-customizations/data.tf @@ -32,4 +32,3 @@ data "local_file" "aft_account_customizations_api_helpers" { data "local_file" "aft_create_pipeline" { filename = "${path.module}/buildspecs/aft-create-pipeline.yml" } - diff --git a/modules/aft-customizations/iam/role-policies/aft_codebuild_customizations_policy.tpl b/modules/aft-customizations/iam/role-policies/aft_codebuild_customizations_policy.tpl index 3d3aa10f..5ec934d1 100644 --- a/modules/aft-customizations/iam/role-policies/aft_codebuild_customizations_policy.tpl +++ b/modules/aft-customizations/iam/role-policies/aft_codebuild_customizations_policy.tpl @@ -106,4 +106,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/role-policies/aft_customizations_invoke_account_provisioning.tpl b/modules/aft-customizations/iam/role-policies/aft_customizations_invoke_account_provisioning.tpl index 16156ec5..118bf291 100644 --- a/modules/aft-customizations/iam/role-policies/aft_customizations_invoke_account_provisioning.tpl +++ b/modules/aft-customizations/iam/role-policies/aft_customizations_invoke_account_provisioning.tpl @@ -36,4 +36,4 @@ "Resource": "${data_aws_kms_alias_aft_key_target_key_arn}" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/role-policies/aft_execute_pipeline_lambda.tpl b/modules/aft-customizations/iam/role-policies/aft_execute_pipeline_lambda.tpl index 0f6e60bd..c19d305b 100644 --- a/modules/aft-customizations/iam/role-policies/aft_execute_pipeline_lambda.tpl +++ b/modules/aft-customizations/iam/role-policies/aft_execute_pipeline_lambda.tpl @@ -32,4 +32,4 @@ "Resource": "*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/role-policies/aft_get_pipeline_status_lambda.tpl b/modules/aft-customizations/iam/role-policies/aft_get_pipeline_status_lambda.tpl index 9208fe46..7df50383 100644 --- a/modules/aft-customizations/iam/role-policies/aft_get_pipeline_status_lambda.tpl +++ b/modules/aft-customizations/iam/role-policies/aft_get_pipeline_status_lambda.tpl @@ -23,4 +23,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/role-policies/aft_states_invoke_customizations_policy.tpl b/modules/aft-customizations/iam/role-policies/aft_states_invoke_customizations_policy.tpl index 6de7c28a..4d11b0c2 100644 --- a/modules/aft-customizations/iam/role-policies/aft_states_invoke_customizations_policy.tpl +++ b/modules/aft-customizations/iam/role-policies/aft_states_invoke_customizations_policy.tpl @@ -20,4 +20,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl b/modules/aft-customizations/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl index b416d239..200f3a0b 100644 --- a/modules/aft-customizations/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl +++ b/modules/aft-customizations/iam/role-policies/ct_aft_codebuild_oss_backend_policy.tpl @@ -35,4 +35,4 @@ "Resource": "arn:aws:kms:${data_aws_region_current_name}:${data_aws_caller_identity_current_account_id}:key/${aws_s3_bucket_aft_terraform_oss_kms_key_id}" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/trust-policies/codebuild.tpl b/modules/aft-customizations/iam/trust-policies/codebuild.tpl index 23e96ea1..3af7c641 100644 --- a/modules/aft-customizations/iam/trust-policies/codebuild.tpl +++ b/modules/aft-customizations/iam/trust-policies/codebuild.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/trust-policies/codepipeline.tpl b/modules/aft-customizations/iam/trust-policies/codepipeline.tpl index be32a2ea..9be3f72b 100644 --- a/modules/aft-customizations/iam/trust-policies/codepipeline.tpl +++ b/modules/aft-customizations/iam/trust-policies/codepipeline.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/trust-policies/lambda.tpl b/modules/aft-customizations/iam/trust-policies/lambda.tpl index e1a853ce..fb84ae9d 100644 --- a/modules/aft-customizations/iam/trust-policies/lambda.tpl +++ b/modules/aft-customizations/iam/trust-policies/lambda.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/iam/trust-policies/states.tpl b/modules/aft-customizations/iam/trust-policies/states.tpl index 195191c8..9217421a 100644 --- a/modules/aft-customizations/iam/trust-policies/states.tpl +++ b/modules/aft-customizations/iam/trust-policies/states.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/lambda/aft-customizations-identify-targets/schema/example_event.json b/modules/aft-customizations/lambda/aft-customizations-identify-targets/schema/example_event.json index 1dcdd245..1b9dccb0 100644 --- a/modules/aft-customizations/lambda/aft-customizations-identify-targets/schema/example_event.json +++ b/modules/aft-customizations/lambda/aft-customizations-identify-targets/schema/example_event.json @@ -5,4 +5,4 @@ "target_value": [ "542113367756","544582079943","516997289537","361126697434"] } ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/locals.tf b/modules/aft-customizations/locals.tf index a94d50e8..59c5315e 100644 --- a/modules/aft-customizations/locals.tf +++ b/modules/aft-customizations/locals.tf @@ -1,3 +1,3 @@ locals { lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/outputs.tf b/modules/aft-customizations/outputs.tf index d91627bb..f0fc2df5 100644 --- a/modules/aft-customizations/outputs.tf +++ b/modules/aft-customizations/outputs.tf @@ -16,4 +16,4 @@ output "aft_codepipeline_customizations_bucket_name" { output "aft_codepipeline_customizations_bucket_arn" { value = aws_s3_bucket.aft_codepipeline_customizations_bucket.arn -} \ No newline at end of file +} diff --git a/modules/aft-customizations/pytest.ini b/modules/aft-customizations/pytest.ini index 48647d5c..e68a4f76 100644 --- a/modules/aft-customizations/pytest.ini +++ b/modules/aft-customizations/pytest.ini @@ -10,4 +10,4 @@ markers = unit integration e2e - slow \ No newline at end of file + slow diff --git a/modules/aft-customizations/s3.tf b/modules/aft-customizations/s3.tf index 05587160..9cb0952b 100644 --- a/modules/aft-customizations/s3.tf +++ b/modules/aft-customizations/s3.tf @@ -14,4 +14,4 @@ resource "aws_s3_bucket" "aft_codepipeline_customizations_bucket" { } } } -} \ No newline at end of file +} diff --git a/modules/aft-customizations/states.tf b/modules/aft-customizations/states.tf index 2f3f2cad..0ab3583b 100644 --- a/modules/aft-customizations/states.tf +++ b/modules/aft-customizations/states.tf @@ -16,4 +16,3 @@ resource "aws_sfn_state_machine" "aft_invoke_customizations_sfn" { role_arn = aws_iam_role.aft_invoke_customizations_sfn.arn definition = templatefile(local.state_machine_source, local.replacements_map) } - diff --git a/modules/aft-customizations/states/invoke_customizations.asl.json b/modules/aft-customizations/states/invoke_customizations.asl.json index efc7c08a..c8757ae3 100644 --- a/modules/aft-customizations/states/invoke_customizations.asl.json +++ b/modules/aft-customizations/states/invoke_customizations.asl.json @@ -98,4 +98,4 @@ "Type": "Fail" } } -} \ No newline at end of file +} diff --git a/modules/aft-customizations/tests/testdata/expected_resources.json b/modules/aft-customizations/tests/testdata/expected_resources.json index 959e1b77..a58c1cf9 100644 --- a/modules/aft-customizations/tests/testdata/expected_resources.json +++ b/modules/aft-customizations/tests/testdata/expected_resources.json @@ -61,4 +61,4 @@ 1 ] ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/tests/testdata/expected_resources_by_module.json b/modules/aft-customizations/tests/testdata/expected_resources_by_module.json index 81aa46e2..903a2262 100644 --- a/modules/aft-customizations/tests/testdata/expected_resources_by_module.json +++ b/modules/aft-customizations/tests/testdata/expected_resources_by_module.json @@ -3,4 +3,4 @@ 0, 38 ] -} \ No newline at end of file +} diff --git a/modules/aft-customizations/tests/testdata/terraform/main.tf b/modules/aft-customizations/tests/testdata/terraform/main.tf index 39c894c8..c4078899 100644 --- a/modules/aft-customizations/tests/testdata/terraform/main.tf +++ b/modules/aft-customizations/tests/testdata/terraform/main.tf @@ -2,4 +2,4 @@ resource "aws_ssm_parameter" "hello_world" { name = "/aft-global-customizations/terraform/unittests/hello-world" type = "String" value = "Hello World!" -} \ No newline at end of file +} diff --git a/modules/aft-customizations/tests/testdata/terraform/providers.tf b/modules/aft-customizations/tests/testdata/terraform/providers.tf index 49a89fdc..6f40fac8 100644 --- a/modules/aft-customizations/tests/testdata/terraform/providers.tf +++ b/modules/aft-customizations/tests/testdata/terraform/providers.tf @@ -8,4 +8,4 @@ provider "aws" { managed_by = "AFT" } } -} \ No newline at end of file +} diff --git a/modules/aft-customizations/variables.tf b/modules/aft-customizations/variables.tf index 5dc189b8..34e56a68 100644 --- a/modules/aft-customizations/variables.tf +++ b/modules/aft-customizations/variables.tf @@ -6,7 +6,18 @@ variable "aft_kms_key_id" { type = string } + + variable "aft_kms_key_arn" { + + + + + + + + + type = string } @@ -92,4 +103,4 @@ variable "maximum_concurrent_customizations" { variable "invoke_account_provisioning_sfn_arn" { type = string -} \ No newline at end of file +} diff --git a/modules/aft-customizations/versions.tf b/modules/aft-customizations/versions.tf index 18d8cbb8..60b8c501 100755 --- a/modules/aft-customizations/versions.tf +++ b/modules/aft-customizations/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/data.tf b/modules/aft-feature-options/data.tf index 671c7245..95484763 100644 --- a/modules/aft-feature-options/data.tf +++ b/modules/aft-feature-options/data.tf @@ -30,4 +30,4 @@ data "aws_iam_policy" "AWSLambdaVPCAccessExecutionRole" { data "aws_organizations_organization" "aft_organization" { provider = aws.ct_management -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam.tf b/modules/aft-feature-options/iam.tf index 762cca3b..8612df9e 100644 --- a/modules/aft-feature-options/iam.tf +++ b/modules/aft-feature-options/iam.tf @@ -109,4 +109,4 @@ resource "aws_iam_role_policy_attachment" "aft_enable_cloudtrail" { count = length(local.lambda_managed_policies) role = aws_iam_role.aft_enable_cloudtrail.name policy_arn = local.lambda_managed_policies[count.index] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/role-policies/aft_delete_default_vpc_lambda.tpl b/modules/aft-feature-options/iam/role-policies/aft_delete_default_vpc_lambda.tpl index f6dd8148..8cb7d71c 100644 --- a/modules/aft-feature-options/iam/role-policies/aft_delete_default_vpc_lambda.tpl +++ b/modules/aft-feature-options/iam/role-policies/aft_delete_default_vpc_lambda.tpl @@ -54,4 +54,4 @@ "Resource" : "*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/role-policies/aft_enable_cloudtrail.tpl b/modules/aft-feature-options/iam/role-policies/aft_enable_cloudtrail.tpl index 473ab443..d76eebf0 100644 --- a/modules/aft-feature-options/iam/role-policies/aft_enable_cloudtrail.tpl +++ b/modules/aft-feature-options/iam/role-policies/aft_enable_cloudtrail.tpl @@ -34,4 +34,4 @@ "Resource" : "*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/role-policies/aft_enroll_support.tpl b/modules/aft-feature-options/iam/role-policies/aft_enroll_support.tpl index 473ab443..d76eebf0 100644 --- a/modules/aft-feature-options/iam/role-policies/aft_enroll_support.tpl +++ b/modules/aft-feature-options/iam/role-policies/aft_enroll_support.tpl @@ -34,4 +34,4 @@ "Resource" : "*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/role-policies/aft_features_states.tpl b/modules/aft-feature-options/iam/role-policies/aft_features_states.tpl index 532b2114..6c85871d 100644 --- a/modules/aft-feature-options/iam/role-policies/aft_features_states.tpl +++ b/modules/aft-feature-options/iam/role-policies/aft_features_states.tpl @@ -16,4 +16,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/trust-policies/lambda.tpl b/modules/aft-feature-options/iam/trust-policies/lambda.tpl index e1a853ce..fb84ae9d 100644 --- a/modules/aft-feature-options/iam/trust-policies/lambda.tpl +++ b/modules/aft-feature-options/iam/trust-policies/lambda.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/iam/trust-policies/states.tpl b/modules/aft-feature-options/iam/trust-policies/states.tpl index 195191c8..9217421a 100644 --- a/modules/aft-feature-options/iam/trust-policies/states.tpl +++ b/modules/aft-feature-options/iam/trust-policies/states.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/kms.tf b/modules/aft-feature-options/kms.tf index 0ce407cf..b409cfaf 100644 --- a/modules/aft-feature-options/kms.tf +++ b/modules/aft-feature-options/kms.tf @@ -12,4 +12,4 @@ resource "aws_kms_alias" "aft_log_key_alias" { name = "alias/aft" target_key_id = aws_kms_key.aft_log_key.key_id -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/kms/key-policies/log-key.tpl b/modules/aft-feature-options/kms/key-policies/log-key.tpl index 8dce373a..5edccce6 100644 --- a/modules/aft-feature-options/kms/key-policies/log-key.tpl +++ b/modules/aft-feature-options/kms/key-policies/log-key.tpl @@ -30,4 +30,4 @@ "Resource": "*" } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/locals.tf b/modules/aft-feature-options/locals.tf index a94d50e8..59c5315e 100644 --- a/modules/aft-feature-options/locals.tf +++ b/modules/aft-feature-options/locals.tf @@ -1,3 +1,3 @@ locals { lambda_managed_policies = [data.aws_iam_policy.AWSLambdaBasicExecutionRole.arn, data.aws_iam_policy.AWSLambdaVPCAccessExecutionRole.arn] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/outputs.tf b/modules/aft-feature-options/outputs.tf index 2538d469..8f637da6 100644 --- a/modules/aft-feature-options/outputs.tf +++ b/modules/aft-feature-options/outputs.tf @@ -31,4 +31,4 @@ output "aws_aft_logs_s3_bucket_region" { output "aws_aft_log_key_arn" { value = aws_kms_key.aft_log_key.arn description = "The ARN of the KMS key used to encrypt contents in the Log bucket" -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/s3/bucket-policies/aft_logging_bucket.tpl b/modules/aft-feature-options/s3/bucket-policies/aft_logging_bucket.tpl index 4a940a80..bb951031 100644 --- a/modules/aft-feature-options/s3/bucket-policies/aft_logging_bucket.tpl +++ b/modules/aft-feature-options/s3/bucket-policies/aft_logging_bucket.tpl @@ -64,4 +64,4 @@ } } ] -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/states.tf b/modules/aft-feature-options/states.tf index f6dba649..bc2bb9b1 100644 --- a/modules/aft-feature-options/states.tf +++ b/modules/aft-feature-options/states.tf @@ -14,4 +14,4 @@ resource "aws_sfn_state_machine" "aft_features" { name = var.aft_features_sfn_name role_arn = aws_iam_role.aft_features_sfn.arn definition = templatefile(local.state_machine_source, local.replacements_map) -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/states/aft_features.asl.json b/modules/aft-feature-options/states/aft_features.asl.json index 43a20278..73d9b97e 100644 --- a/modules/aft-feature-options/states/aft_features.asl.json +++ b/modules/aft-feature-options/states/aft_features.asl.json @@ -59,4 +59,4 @@ "Type": "Fail" } } -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/variables.tf b/modules/aft-feature-options/variables.tf index 841a990e..75264df1 100644 --- a/modules/aft-feature-options/variables.tf +++ b/modules/aft-feature-options/variables.tf @@ -44,4 +44,4 @@ variable "log_archive_account_id" { variable "aft_features_sfn_name" { type = string -} \ No newline at end of file +} diff --git a/modules/aft-feature-options/versions.tf b/modules/aft-feature-options/versions.tf index 1fdff71f..fdc09c26 100755 --- a/modules/aft-feature-options/versions.tf +++ b/modules/aft-feature-options/versions.tf @@ -8,4 +8,4 @@ terraform { configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management] } } -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/admin-role/iam.tf b/modules/aft-iam-roles/admin-role/iam.tf index e512aaa4..e132eaf8 100644 --- a/modules/aft-iam-roles/admin-role/iam.tf +++ b/modules/aft-iam-roles/admin-role/iam.tf @@ -27,4 +27,4 @@ resource "aws_iam_role" "role" { output "arn" { value = aws_iam_role.role.arn -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/admin-role/main.tf b/modules/aft-iam-roles/admin-role/main.tf index 870bfb29..ed160fde 100644 --- a/modules/aft-iam-roles/admin-role/main.tf +++ b/modules/aft-iam-roles/admin-role/main.tf @@ -5,4 +5,4 @@ terraform { version = ">= 2.7.0" } } -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/admin-role/trust_policy.tpl b/modules/aft-iam-roles/admin-role/trust_policy.tpl index 4d277b80..24c46e8f 100644 --- a/modules/aft-iam-roles/admin-role/trust_policy.tpl +++ b/modules/aft-iam-roles/admin-role/trust_policy.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/iam/aft_admin_role_policy.tpl b/modules/aft-iam-roles/iam/aft_admin_role_policy.tpl index 721236b1..5ed14533 100644 --- a/modules/aft-iam-roles/iam/aft_admin_role_policy.tpl +++ b/modules/aft-iam-roles/iam/aft_admin_role_policy.tpl @@ -7,4 +7,4 @@ "Resource": "arn:aws:iam::*:role/AWSAFTExecution" } ] -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/iam/aft_admin_role_trust_policy.tpl b/modules/aft-iam-roles/iam/aft_admin_role_trust_policy.tpl index c8e75416..e068bc65 100644 --- a/modules/aft-iam-roles/iam/aft_admin_role_trust_policy.tpl +++ b/modules/aft-iam-roles/iam/aft_admin_role_trust_policy.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/outputs.tf b/modules/aft-iam-roles/outputs.tf index f11a4f48..e1d451a0 100644 --- a/modules/aft-iam-roles/outputs.tf +++ b/modules/aft-iam-roles/outputs.tf @@ -12,4 +12,4 @@ output "audit_exec_role_arn" { } output "aft_exec_role_arn" { value = module.aft_exec_role.arn -} \ No newline at end of file +} diff --git a/modules/aft-iam-roles/versions.tf b/modules/aft-iam-roles/versions.tf index 1fdff71f..fdc09c26 100755 --- a/modules/aft-iam-roles/versions.tf +++ b/modules/aft-iam-roles/versions.tf @@ -8,4 +8,4 @@ terraform { configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management] } } -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/buildspecs/aft-lambda-layer.yml b/modules/aft-lambda-layer/buildspecs/aft-lambda-layer.yml index af3130ae..bfe84af7 100644 --- a/modules/aft-lambda-layer/buildspecs/aft-lambda-layer.yml +++ b/modules/aft-lambda-layer/buildspecs/aft-lambda-layer.yml @@ -11,6 +11,8 @@ phases: - DEFAULT_PATH=$(pwd) - AWS_MODULE_SOURCE=$(aws ssm get-parameter --name $SSM_AWS_MODULE_SOURCE --query "Parameter.Value" --output text) - AWS_MODULE_GIT_REF=$(aws ssm get-parameter --name $SSM_AWS_MODULE_GIT_REF --query "Parameter.Value" --output text) + # URL Without Access ID + - URL=$(echo "$AWS_MODULE_SOURCE" | awk '{split($0,a,"@"); print a[2]}') - | ssh_key_parameter=$(aws ssm get-parameter --name /aft/config/aft-ssh-key --with-decryption || echo "None") if [[ $ssh_key_parameter != "None" ]]; then @@ -27,6 +29,7 @@ phases: fi - git config --global credential.helper '!aws codecommit credential-helper $@' - git config --global credential.UseHttpPath true + - echo "Building aft_common from ${URL}:${AWS_MODULE_GIT_REF}" - git clone -b $AWS_MODULE_GIT_REF $AWS_MODULE_SOURCE aws-aft-core-framework - python3 -m pip install virtualenv - python3 -m venv .venv @@ -38,4 +41,4 @@ phases: - ls - mv -v ./.venv/lib/ ./python/ - zip -r layer.zip python - - aws s3 cp layer.zip s3://${BUCKET_NAME}/layer.zip \ No newline at end of file + - aws s3 cp layer.zip s3://${BUCKET_NAME}/layer.zip diff --git a/modules/aft-lambda-layer/cloudwatch.tf b/modules/aft-lambda-layer/cloudwatch.tf index 54b60853..99a7008d 100644 --- a/modules/aft-lambda-layer/cloudwatch.tf +++ b/modules/aft-lambda-layer/cloudwatch.tf @@ -13,4 +13,4 @@ resource "aws_cloudwatch_event_target" "codebuild_trigger" { target_id = local.target_id arn = aws_codebuild_project.codebuild.id role_arn = aws_iam_role.codebuild.arn -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/codebuild.tf b/modules/aft-lambda-layer/codebuild.tf index dafe46e5..875cc9a9 100644 --- a/modules/aft-lambda-layer/codebuild.tf +++ b/modules/aft-lambda-layer/codebuild.tf @@ -70,4 +70,4 @@ resource "aws_codebuild_project" "codebuild" { security_group_ids = var.aft_vpc_default_sg } -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/iam.tf b/modules/aft-lambda-layer/iam.tf index 08742920..19e44503 100644 --- a/modules/aft-lambda-layer/iam.tf +++ b/modules/aft-lambda-layer/iam.tf @@ -13,4 +13,4 @@ resource "aws_iam_role_policy" "codebuild" { "cloudwatch_event_name" = local.common_name "data_aws_kms_alias_aft_key_target_key_arn" = var.aft_kms_key_arn }) -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/iam/trust-policies/codebuild.tpl b/modules/aft-lambda-layer/iam/trust-policies/codebuild.tpl index dbdfd0fc..0b01b6b6 100644 --- a/modules/aft-lambda-layer/iam/trust-policies/codebuild.tpl +++ b/modules/aft-lambda-layer/iam/trust-policies/codebuild.tpl @@ -12,4 +12,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/locals.tf b/modules/aft-lambda-layer/locals.tf index 6f256c7b..b5ad9671 100644 --- a/modules/aft-lambda-layer/locals.tf +++ b/modules/aft-lambda-layer/locals.tf @@ -2,4 +2,4 @@ locals { common_name = "python-layer-builder-${var.lambda_layer_name}-${random_string.resource_suffix.result}" account_id = data.aws_caller_identity.session.account_id target_id = "trigger_build" -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/outputs.tf b/modules/aft-lambda-layer/outputs.tf index 6255ecb1..9b9337b7 100644 --- a/modules/aft-lambda-layer/outputs.tf +++ b/modules/aft-lambda-layer/outputs.tf @@ -1,3 +1,3 @@ output "layer_version_arn" { value = aws_lambda_layer_version.layer_version.arn -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/readme.md b/modules/aft-lambda-layer/readme.md index 71e07d19..6ed460d4 100644 --- a/modules/aft-lambda-layer/readme.md +++ b/modules/aft-lambda-layer/readme.md @@ -28,4 +28,4 @@ github_token - Set $TF_VAR_github_token to securely configure this variable with # Outputs -layer_version_arn - the ARN of the lambda layer version \ No newline at end of file +layer_version_arn - the ARN of the lambda layer version diff --git a/modules/aft-lambda-layer/variables.tf b/modules/aft-lambda-layer/variables.tf index b99aaab3..b0f677c4 100644 --- a/modules/aft-lambda-layer/variables.tf +++ b/modules/aft-lambda-layer/variables.tf @@ -47,4 +47,4 @@ variable "aft_vpc_default_sg" { } variable "aft_version" { type = string -} \ No newline at end of file +} diff --git a/modules/aft-lambda-layer/versions.tf b/modules/aft-lambda-layer/versions.tf index 18d8cbb8..60b8c501 100755 --- a/modules/aft-lambda-layer/versions.tf +++ b/modules/aft-lambda-layer/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/modules/aft-ssm-parameters/ssm.tf b/modules/aft-ssm-parameters/ssm.tf index 579e8179..820d1f48 100644 --- a/modules/aft-ssm-parameters/ssm.tf +++ b/modules/aft-ssm-parameters/ssm.tf @@ -362,4 +362,4 @@ resource "aws_ssm_parameter" "aft_maximum_concurrent_customizations" { name = "/aft/config/customizations/maximum_concurrent_customizations" value = var.maximum_concurrent_customizations type = "String" -} \ No newline at end of file +} diff --git a/modules/aft-ssm-parameters/variables.tf b/modules/aft-ssm-parameters/variables.tf index c79ae672..f6848e54 100644 --- a/modules/aft-ssm-parameters/variables.tf +++ b/modules/aft-ssm-parameters/variables.tf @@ -245,4 +245,4 @@ variable "maximum_concurrent_customizations" { variable "aft_version" { type = string -} \ No newline at end of file +} diff --git a/modules/aft-ssm-parameters/versions.tf b/modules/aft-ssm-parameters/versions.tf index 18d8cbb8..60b8c501 100755 --- a/modules/aft-ssm-parameters/versions.tf +++ b/modules/aft-ssm-parameters/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/outputs.tf b/outputs.tf index 6bcbfa5e..9b5f7db3 100644 --- a/outputs.tf +++ b/outputs.tf @@ -141,4 +141,4 @@ output "aft_vpc_public_subnet_01_cidr" { output "aft_vpc_public_subnet_02_cidr" { value = var.aft_vpc_public_subnet_02_cidr -} \ No newline at end of file +} diff --git a/providers.tf b/providers.tf index ba4369f2..6a0ae12b 100644 --- a/providers.tf +++ b/providers.tf @@ -60,4 +60,4 @@ provider "aws" { managed_by = "AFT" } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-common/templates/customizations_pipeline/aft-providers.jinja b/sources/aft-customizations-common/templates/customizations_pipeline/aft-providers.jinja index 274ee7b4..a24d62d5 100644 --- a/sources/aft-customizations-common/templates/customizations_pipeline/aft-providers.jinja +++ b/sources/aft-customizations-common/templates/customizations_pipeline/aft-providers.jinja @@ -12,4 +12,4 @@ provider "aws" { managed_by = "AFT" } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-common/templates/customizations_pipeline/backend.jinja b/sources/aft-customizations-common/templates/customizations_pipeline/backend.jinja index a1c93c42..1dfe84ef 100644 --- a/sources/aft-customizations-common/templates/customizations_pipeline/backend.jinja +++ b/sources/aft-customizations-common/templates/customizations_pipeline/backend.jinja @@ -12,4 +12,4 @@ terraform { kms_key_id = "{{ kms_key_id }}" profile = "aft-management" } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-common/templates/customizations_pipeline/data.tf b/sources/aft-customizations-common/templates/customizations_pipeline/data.tf index 08131e3f..0a3a10b2 100644 --- a/sources/aft-customizations-common/templates/customizations_pipeline/data.tf +++ b/sources/aft-customizations-common/templates/customizations_pipeline/data.tf @@ -41,4 +41,4 @@ data "aws_s3_bucket" "aft_codepipeline_customizations_bucket" { data "aws_ssm_parameter" "vcs_provider" { name = "/aft/config/vcs/provider" -} \ No newline at end of file +} diff --git a/sources/aft-customizations-common/templates/customizations_pipeline/versions.tf b/sources/aft-customizations-common/templates/customizations_pipeline/versions.tf index 18d8cbb8..60b8c501 100644 --- a/sources/aft-customizations-common/templates/customizations_pipeline/versions.tf +++ b/sources/aft-customizations-common/templates/customizations_pipeline/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/post-api-helpers.sh b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/post-api-helpers.sh index 79f1e9a6..603f3289 100644 --- a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/post-api-helpers.sh +++ b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/post-api-helpers.sh @@ -1,3 +1,3 @@ #!/bin/bash -echo "Executing Post-API Helpers" \ No newline at end of file +echo "Executing Post-API Helpers" diff --git a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/pre-api-helpers.sh b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/pre-api-helpers.sh index fd2dc402..b34f82e6 100644 --- a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/pre-api-helpers.sh +++ b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/api_helpers/pre-api-helpers.sh @@ -1,3 +1,3 @@ #!/bin/bash -echo "Executing Pre-API Helpers" \ No newline at end of file +echo "Executing Pre-API Helpers" diff --git a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/backend.jinja b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/backend.jinja index 1b7ec6e1..90ac3de3 100644 --- a/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/backend.jinja +++ b/sources/aft-customizations-repos/aft-account-customizations/ACCOUNT_TEMPLATE/terraform/backend.jinja @@ -23,4 +23,4 @@ terraform { } } } -{% endif %} \ No newline at end of file +{% endif %} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/README.md b/sources/aft-customizations-repos/aft-account-provisioning-customizations/README.md index 09540086..0d376388 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/README.md +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/README.md @@ -120,4 +120,3 @@ def lambda_handler(event, context): ) ``` - diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/backend.jinja b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/backend.jinja index 1b7ec6e1..90ac3de3 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/backend.jinja +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/backend.jinja @@ -23,4 +23,4 @@ terraform { } } } -{% endif %} \ No newline at end of file +{% endif %} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam.tf b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam.tf index 1652120c..9efae6bb 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam.tf +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam.tf @@ -10,4 +10,4 @@ resource "aws_iam_role_policy" "aft_states" { policy = templatefile("${path.module}/iam/role-policies/iam-aft-states.tpl", { account_provisioning_customizations_sfn_arn = aws_sfn_state_machine.aft_account_provisioning_customizations.arn }) -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/role-policies/iam-aft-states.tpl b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/role-policies/iam-aft-states.tpl index affac3ee..b6a70dfa 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/role-policies/iam-aft-states.tpl +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/role-policies/iam-aft-states.tpl @@ -7,4 +7,4 @@ "Resource": "${account_provisioning_customizations_sfn_arn}" } ] -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/trust-policies/states.tpl b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/trust-policies/states.tpl index 195191c8..9217421a 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/trust-policies/states.tpl +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/iam/trust-policies/states.tpl @@ -9,4 +9,4 @@ "Action": "sts:AssumeRole" } ] -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states.tf b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states.tf index 127c2da7..4c12cf7b 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states.tf +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states.tf @@ -2,4 +2,4 @@ resource "aws_sfn_state_machine" "aft_account_provisioning_customizations" { name = "aft-account-provisioning-customizations" role_arn = aws_iam_role.aft_states.arn definition = templatefile("${path.module}/states/customizations.asl.json", {}) -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states/customizations.asl.json b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states/customizations.asl.json index de6c35fe..3f3fc1a7 100644 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states/customizations.asl.json +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/states/customizations.asl.json @@ -6,4 +6,4 @@ "End": true } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/versions.tf b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/versions.tf index 18d8cbb8..60b8c501 100755 --- a/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/versions.tf +++ b/sources/aft-customizations-repos/aft-account-provisioning-customizations/terraform/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-request/README.md b/sources/aft-customizations-repos/aft-account-request/README.md index a305dc5f..92cb3193 100644 --- a/sources/aft-customizations-repos/aft-account-request/README.md +++ b/sources/aft-customizations-repos/aft-account-request/README.md @@ -13,8 +13,6 @@ Git push action will trigger ct-aft-account-request AWS CodePipeline in AFT mana - **module source** is path to Account Request terraform module provided by AFT - this should always be ```source = "./modules/aft-account-request"``` -- **create_customizations** must **** be set to true if you intend to customize account after provisioning. Refer to documentation for more information. - - **control_tower_parameters** captures mandatory inputs listed below to create AWS Control Tower managed account. - AccountEmail - AccountName @@ -46,4 +44,4 @@ AFT supports updating of all non control_tower_parameters inputs and ManagedOrga Although AWS Control Tower Account Factory can process single request at any given time, AFT pipeline allows you to submit multiple Account Requests and queues all the requests to be processed by AWS Control Tower Account Factory in FIFO order. -You can create Account Request Terraform file per account or cascade multiple requests in a single Terraform file. \ No newline at end of file +You can create Account Request Terraform file per account or cascade multiple requests in a single Terraform file. diff --git a/sources/aft-customizations-repos/aft-account-request/terraform/backend.jinja b/sources/aft-customizations-repos/aft-account-request/terraform/backend.jinja index 1b7ec6e1..90ac3de3 100644 --- a/sources/aft-customizations-repos/aft-account-request/terraform/backend.jinja +++ b/sources/aft-customizations-repos/aft-account-request/terraform/backend.jinja @@ -23,4 +23,4 @@ terraform { } } } -{% endif %} \ No newline at end of file +{% endif %} diff --git a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/ddb.tf b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/ddb.tf index 4dc7eb21..4cae37ec 100644 --- a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/ddb.tf +++ b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/ddb.tf @@ -22,4 +22,4 @@ resource "aws_dynamodb_table_item" "account-request" { account_customizations_name = { S = var.account_customizations_name } custom_fields = { S = jsonencode(var.custom_fields) } }) -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/variables.tf b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/variables.tf index d496b106..458f9131 100755 --- a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/variables.tf +++ b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/variables.tf @@ -44,4 +44,3 @@ variable "account_customizations_name" { default = "" description = "The name of the account customizations to apply" } - diff --git a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/versions.tf b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/versions.tf index 18d8cbb8..60b8c501 100755 --- a/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/versions.tf +++ b/sources/aft-customizations-repos/aft-account-request/terraform/modules/aft-account-request/versions.tf @@ -7,4 +7,4 @@ terraform { version = ">= 3.15" } } -} \ No newline at end of file +} diff --git a/sources/aft-customizations-repos/aft-global-customizations/README.md b/sources/aft-customizations-repos/aft-global-customizations/README.md index 68028033..568befd7 100644 --- a/sources/aft-customizations-repos/aft-global-customizations/README.md +++ b/sources/aft-customizations-repos/aft-global-customizations/README.md @@ -37,4 +37,4 @@ account = $(aws sts get-caller-identity --query Account --output text) region = $(aws ec2 describe-availability-zones --query 'AvailabilityZones[0].[RegionName]' --output text) cidr = $(python ./python/source/get_cidr_range.py) aws ssm put-parameter --name /$account/$region/vpc/cidr --value $cidr -``` \ No newline at end of file +``` diff --git a/sources/aft-customizations-repos/aft-global-customizations/api_helpers/post-api-helpers.sh b/sources/aft-customizations-repos/aft-global-customizations/api_helpers/post-api-helpers.sh index 79f1e9a6..603f3289 100644 --- a/sources/aft-customizations-repos/aft-global-customizations/api_helpers/post-api-helpers.sh +++ b/sources/aft-customizations-repos/aft-global-customizations/api_helpers/post-api-helpers.sh @@ -1,3 +1,3 @@ #!/bin/bash -echo "Executing Post-API Helpers" \ No newline at end of file +echo "Executing Post-API Helpers" diff --git a/sources/aft-customizations-repos/aft-global-customizations/api_helpers/pre-api-helpers.sh b/sources/aft-customizations-repos/aft-global-customizations/api_helpers/pre-api-helpers.sh index fd2dc402..b34f82e6 100644 --- a/sources/aft-customizations-repos/aft-global-customizations/api_helpers/pre-api-helpers.sh +++ b/sources/aft-customizations-repos/aft-global-customizations/api_helpers/pre-api-helpers.sh @@ -1,3 +1,3 @@ #!/bin/bash -echo "Executing Pre-API Helpers" \ No newline at end of file +echo "Executing Pre-API Helpers" diff --git a/sources/aft-customizations-repos/aft-global-customizations/terraform/backend.jinja b/sources/aft-customizations-repos/aft-global-customizations/terraform/backend.jinja index 1b7ec6e1..90ac3de3 100644 --- a/sources/aft-customizations-repos/aft-global-customizations/terraform/backend.jinja +++ b/sources/aft-customizations-repos/aft-global-customizations/terraform/backend.jinja @@ -23,4 +23,4 @@ terraform { } } } -{% endif %} \ No newline at end of file +{% endif %} diff --git a/sources/aft-lambda-layer/aft_common/aft_utils.py b/sources/aft-lambda-layer/aft_common/aft_utils.py index 4c78e3ef..5a95b024 100644 --- a/sources/aft-lambda-layer/aft_common/aft_utils.py +++ b/sources/aft-lambda-layer/aft_common/aft_utils.py @@ -33,7 +33,10 @@ from mypy_boto3_stepfunctions import SFNClient from mypy_boto3_stepfunctions.type_defs import StartExecutionOutputTypeDef from mypy_boto3_sts import STSClient - from mypy_boto3_sts.type_defs import CredentialsTypeDef + from mypy_boto3_sts.type_defs import ( + AssumeRoleRequestRequestTypeDef, + CredentialsTypeDef, + ) else: LambdaClient = object InvocationResponseTypeDef = object @@ -225,22 +228,24 @@ def get_assume_role_credentials( session_name: str, external_id: Optional[str] = None, session_duration: int = 900, + session_policy: Optional[str] = None, ) -> CredentialsTypeDef: client: STSClient = session.client("sts") + assume_role_params: AssumeRoleRequestRequestTypeDef = { + "RoleArn": role_arn, + "RoleSessionName": session_name, + "DurationSeconds": session_duration, + } + if external_id: - assume_role_response = client.assume_role( - RoleArn=role_arn, - RoleSessionName=session_name, - DurationSeconds=session_duration, - ExternalId=external_id, - ) - else: - assume_role_response = client.assume_role( - RoleArn=role_arn, - RoleSessionName=session_name, - DurationSeconds=session_duration, - ) + assume_role_params.update({"ExternalId": external_id}) + + if session_policy: + assume_role_params.update({"Policy": session_policy}) + + assume_role_response = client.assume_role(**assume_role_params) + credentials = assume_role_response["Credentials"] return credentials @@ -297,6 +302,19 @@ def get_ct_management_session(session: Session) -> Session: return get_boto_session(ct_mgmt_creds) +def get_aft_admin_role_session(session: Session) -> Session: + administrator_role = get_ssm_parameter_value(session, SSM_PARAM_AFT_ADMIN_ROLE) + execution_role = get_ssm_parameter_value(session, SSM_PARAM_AFT_EXEC_ROLE) + session_name = get_ssm_parameter_value(session, SSM_PARAM_AFT_SESSION_NAME) + + # Assume aws-aft-AdministratorRole locally + local_creds = get_assume_role_credentials( + session, build_role_arn(session, administrator_role), session_name + ) + + return get_boto_session(local_creds) + + def get_log_archive_session(session: Session) -> Session: log_archive_account = get_ssm_parameter_value( session, SSM_PARAM_ACCOUNT_LOG_ARCHIVE_ACCOUNT_ID diff --git a/sources/aft-lambda-layer/readme.md b/sources/aft-lambda-layer/readme.md index 04bdf864..f670f952 100644 --- a/sources/aft-lambda-layer/readme.md +++ b/sources/aft-lambda-layer/readme.md @@ -64,4 +64,4 @@ Function list: For more information on the implementation of each of these functions, see the source here: -```modules/lambda_layer/aft_common_layer/python/lib/python3.8/site-packages/aft_common/utils.py``` \ No newline at end of file +```modules/lambda_layer/aft_common_layer/python/lib/python3.8/site-packages/aft_common/utils.py``` diff --git a/sources/aft-lambda-layer/setup.py b/sources/aft-lambda-layer/setup.py index 0f0d603f..7dd03761 100644 --- a/sources/aft-lambda-layer/setup.py +++ b/sources/aft-lambda-layer/setup.py @@ -73,7 +73,7 @@ "pre-commit == 2.16.0", "pycodestyle == 2.8.0", "mypy == 0.930", - "boto3-stubs[support, stepfunctions, ec2, organizations, servicecatalog, sqs, lambda, sns, sts, cloudtrail] == 1.20.26", + "boto3-stubs[support, stepfunctions, ec2, organizations, servicecatalog, sqs, lambda, sns, sts, cloudtrail, ssm, iam] == 1.20.26", "mypy_boto3_builder == 5.5.0", ] }, diff --git a/sources/scripts/creds.sh b/sources/scripts/creds.sh index 6826434c..7391fb01 100755 --- a/sources/scripts/creds.sh +++ b/sources/scripts/creds.sh @@ -142,4 +142,4 @@ if $USER_DEFINED_ACCOUNT || $AFT || $CT; then unset AWS_SECRET_ACCESS_KEY unset AWS_SESSION_TOKEN fi -echo "Script execution complete" \ No newline at end of file +echo "Script execution complete" diff --git a/sources/scripts/terraform_client.py b/sources/scripts/terraform_client.py index ae9a9b2f..48e7567d 100755 --- a/sources/scripts/terraform_client.py +++ b/sources/scripts/terraform_client.py @@ -206,14 +206,18 @@ def __build_standard_headers(api_token): def __post(endpoint, headers, payload): tf_dist = os.environ.get("TF_DISTRIBUTION") - response = requests.post(endpoint, headers=headers, json=payload, verify=tf_dist != "tfe") + response = requests.post( + endpoint, headers=headers, json=payload, verify=tf_dist != "tfe" + ) __handle_errors(response) return response.json() def __patch(endpoint, headers, payload): tf_dist = os.environ.get("TF_DISTRIBUTION") - response = requests.patch(endpoint, headers=headers, json=payload, verify=tf_dist != "tfe") + response = requests.patch( + endpoint, headers=headers, json=payload, verify=tf_dist != "tfe" + ) __handle_errors(response) return response.json() diff --git a/versions.tf b/versions.tf index a0d399e6..04f6ee0c 100644 --- a/versions.tf +++ b/versions.tf @@ -8,4 +8,4 @@ terraform { configuration_aliases = [aws.ct_management, aws.log_archive, aws.audit, aws.aft_management, aws.tf_backend_secondary_region] } } -} \ No newline at end of file +}