[
  {
    "path": ".devcontainer/Dockerfile",
    "content": "FROM stelligent/vscode-remote-config-lint:latest\n"
  },
  {
    "path": ".devcontainer/build/Dockerfile",
    "content": "FROM ubuntu:latest\n\n# Avoid warnings by switching to noninteractive\nENV DEBIAN_FRONTEND=noninteractive\n\n# Install packages\nRUN apt-get update \\\n\t\t# Install apt-utils and suppress package configuration warning\n\t\t&& apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \\\n\t\t# Install build tools\n\t\t&& apt-get install -y \\\n\t\tbc \\\n\t\tunzip \\\n\t\twget \\\n\t\tgit \\\n\t\tg++ \\\n\t\tgcc \\\n\t\tlibc6-dev \\\n\t\tmake \\\n\t\tpkg-config \\\n\t\tca-certificates \\\n\t\tgnupg-agent \\\n\t\t# Cleanup apt lists\n\t\t&& rm -rf /var/lib/apt/lists/*\n\n# Install Go\nENV GOLANG_VERSION 1.13.7\nRUN set -eux; \\\n\tgoRelArch='linux-amd64'; \\\n\tgoRelSha256='b3dd4bd781a0271b33168e627f7f43886b4c5d1c794a4015abf34e99c6526ca3'; \\\n\turl=\"https://golang.org/dl/go${GOLANG_VERSION}.${goRelArch}.tar.gz\"; \\\n\twget -O go.tgz \"$url\"; \\\n\techo \"${goRelSha256} *go.tgz\" | sha256sum -c -; \\\n\ttar -C /usr/local -xzf go.tgz; \\\n\trm go.tgz\nENV GOPATH /go\nENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH\nRUN mkdir -p \"$GOPATH/src\" \"$GOPATH/bin\" && chmod -R 777 \"$GOPATH\"\nENV GO111MODULE=on\n\n# Install VS Code Go Dependencies\nRUN go get -x -d github.com/stamblerre/gocode \\\n\t&& go build -o gocode-gomod github.com/stamblerre/gocode \\\n\t&& mv gocode-gomod $GOPATH/bin/ \\\n\t&& go get -u -v \\\n\tgolang.org/x/tools/gopls \\\n\tgithub.com/mdempsky/gocode \\\n\t# Workaround for https://github.com/uudashr/gopkgs/issues/25\n\tgithub.com/uudashr/gopkgs/v2/cmd/gopkgs \\\n\tgithub.com/sqs/goreturns \\\n\tgolang.org/x/lint/golint \\\n\tgithub.com/ramya-rao-a/go-outline \\\n\t&& go get github.com/go-delve/delve/cmd/dlv\n\n# Create a non-root user\nARG USERNAME=config-lint-dev\nARG USER_UID=1000\nARG USER_GID=$USER_UID\nRUN groupadd --gid $USER_GID $USERNAME \\\n  && useradd --uid $USER_UID --gid $USER_GID --shell /bin/bash -m $USERNAME\n\n# Prompt gpg window inside container for signing commits & setup folder permissions for non-root user\nRUN echo 'export GPG_TTY=\"$(tty)\"' >> /home/$USERNAME/.bashrc \\\n  && mkdir /home/$USERNAME/.gnupg \\\n  && chown -R $USERNAME:$USERNAME /home/$USERNAME/.gnupg\n\n# Persist bash history between runs\nRUN SNIPPET=\"export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history\" \\\n    && mkdir /commandhistory \\\n    && touch /commandhistory/.bash_history \\\n    && chown -R $USERNAME /commandhistory \\\n    && echo $SNIPPET >> \"/home/$USERNAME/.bashrc\"\n\n# Add non-root user to $GOPATH\nRUN chown -R $USERNAME $GOPATH \\\n\t\t# Add write permission for /go/pkg\n\t\t&& chmod -R a+w /go/pkg\n\n# Install Terraform\nARG TERRAFORM_VERSION=0.12.20\nRUN cd /tmp \\\n\t&& wget https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_linux_amd64.zip \\\n\t&& unzip terraform_${TERRAFORM_VERSION}_linux_amd64.zip \\\n\t&& mv terraform /usr/local/bin/ \\\n\t&& rm terraform_${TERRAFORM_VERSION}_linux_amd64.zip\n\n# Enter container as non-root user\nUSER $USERNAME\n"
  },
  {
    "path": ".devcontainer/build/dockerhub.sh",
    "content": "#!/bin/bash -ex\n\nset +x\nif [[ -z ${DOCKER_ORG} ]];\nthen\n  echo DOCKER_ORG must be set in the environment\n  exit 1\nfi\nif [[ -z ${GITHUB_SHA} ]];\nthen\n  echo GITHUB_SHA must be set in the environment\n  exit 1\nfi\nset -x\n\nCOMMIT_HASH=${GITHUB_SHA:0:8}\n\n# publish vscode-remote docker image to DockerHub, https://hub.docker.com/r/stelligent/vscode-remote-config-lint\ndocker build -t $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH} --file .devcontainer/build/Dockerfile .\ndocker tag $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH} $DOCKER_ORG/vscode-remote-config-lint:latest\ndocker push $DOCKER_ORG/vscode-remote-config-lint:${COMMIT_HASH}\ndocker push $DOCKER_ORG/vscode-remote-config-lint:latest"
  },
  {
    "path": ".devcontainer/devcontainer.json",
    "content": "{\n\t\"name\": \"config-lint Development\",\n\t\"dockerFile\": \"Dockerfile\",\n\t\"appPort\": 9000,\n\t\"remote.containers.workspaceMountConsistency\": \"consistent\",\n\t\"mounts\": [\n\t\t// Bash History\n\t\t\"source=config-lint-bash_history,target=/commandhistory,type=volume\"\n\t],\n\t\"runArgs\": [\n\t\t// SSH\n\t\t\"-v\", \"${localEnv:HOME}/.ssh:/home/config-lint-dev/.ssh:ro\",\n\t\t// GPG\n\t\t\"-v\", \"${localEnv:HOME}/.gnupg/private-keys-v1.d:/home/config-lint-dev/.gnupg/private-keys-v1.d:ro\",\n\t\t\"-v\", \"${localEnv:HOME}/.gnupg/pubring.kbx:/home/config-lint-dev/.gnupg/pubring.kbx:ro\",\n\t\t\"-v\", \"${localEnv:HOME}/.gnupg/trustdb.gpg:/home/config-lint-dev/.gnupg/trustdb.gpg:ro\"\n\t],\n\t\"extensions\": [\n\t\t// General\n\t\t\"CoenraadS.bracket-pair-colorizer\",\n\t\t\"fabiospampinato.vscode-diff\",\n\t\t\"mrmlnc.vscode-duplicate\",\n\t\t\"ms-azuretools.vscode-docker\",\n\t\t\"wayou.vscode-todo-highlight\",\n\t\t// Go\n\t\t\"ms-vscode.go\",\n\t\t// Terraform\n\t\t\"mauve.terraform\",\n\t\t// JSON\n\t\t\"mohsen1.prettify-json\",\n\t\t// YAML\n\t\t\"redhat.vscode-yaml\"\n\t],\n\t\"settings\": {\n\t\t// Bracket Pair Colorizer\n\t\t\"bracketPairColorizer.forceUniqueOpeningColor\": false,\n\t\t\"bracketPairColorizer.colorMode\": \"Consecutive\",\n\t\t\"bracketPairColorizer.highlightActiveScope\": true,\n\t\t\"bracketPairColorizer.activeScopeCSS\": [\n\t\t\t\"borderStyle : solid\",\n\t\t\t\"borderWidth : 1px\",\n\t\t\t\"borderColor : {color}; opacity: 0.5\",\n\t\t\t\"backgroundColor : {color}\"\n\t\t],\n\t\t\"editor.matchBrackets\": \"never\",\n\t\t\"bracketPairColorizer.showBracketsInGutter\": true,\n\t\t// Go\n\t\t\"go.gopath\": \"/go\",\n\t\t\"go.inferGopath\": true,\n\t\t\"go.useLanguageServer\": true,\n\t\t\"[go]\": {\n\t\t\t\t\"editor.insertSpaces\": true,\n\t\t\t\t\"editor.tabSize\": 4,\n\t\t\t\t\"editor.formatOnSave\": true,\n\t\t\t\t\"editor.codeActionsOnSave\": {\n\t\t\t\t\t\t\"source.organizeImports\": true\n\t\t\t\t}\n\t\t},\n\t\t\"gopls\": {\n\t\t\t\t\"usePlaceholders\": true\n\t\t},\n\t\t// Terraform\n\t\t\"[terraform]\": {\n\t\t\t\"editor.formatOnSave\": true\n\t\t},\n\t\t\"terraform.languageServer\": {\n\t\t\t\"enabled\": false,\n\t\t\t\"args\": []\n\t\t},\n\t\t\"terraform.indexing\": {\n\t\t\t\"enabled\": false,\n\t\t\t\"liveIndexing\": false\n\t\t},\n\t\t// YAML\n\t\t\"[yaml]\": {\n\t\t\t\"editor.insertSpaces\": true,\n\t\t\t\"editor.tabSize\": 2\n\t\t},\n\t\t\"yaml.format.enable\": true,\n\t\t\"yaml.format.singleQuote\": true,\n\t\t\"yaml.format.bracketSpacing\": true,\n\t\t\"yaml.format.printWidth\": 120,\n\t\t\"yaml.format.proseWrap\": \"always\",\n\t\t// TODO\n\t\t\"todohighlight.isEnable\": true,\n\t\t\"todohighlight.isCaseSensitive\": false\n\t},\n\t\"postCreateCommand\": \"make deps\"\n}\n"
  },
  {
    "path": ".dockerhub/Dockerfile",
    "content": "FROM scratch\nCOPY config-lint /\nENTRYPOINT [\"/config-lint\"] \n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "name: Build\n\non:\n  push:\n    branches-ignore:\n      - 'master'\n    tags-ignore:\n      - '**'\n    paths-ignore:\n      - 'docs/**'\n      - '**.md'\n  pull_request:\n    branches:\n      - master\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - \n        name: checkout\n        uses: actions/checkout@master\n      -\n        name: setup go\n        uses: actions/setup-go@v1\n        with:\n          go-version: '1.13'\n      -\n        name: dependencies\n        run: |\n          go mod download\n      -  \n        name: build\n        run: |\n          export GOPATH=/home/runner/go\n          export PATH=\"$PATH:$GOPATH/bin\"\n          make build\n      -  \n        name: test\n        run: |\n          export GOPATH=/home/runner/go\n          export PATH=\"$PATH:$GOPATH/bin\"\n          make test\n          make smoke-test\n"
  },
  {
    "path": ".github/workflows/build_and_deploy.yml",
    "content": "name: Build & Deploy\n\non:\n  push:\n    tags:\n      - 'v*.*.*'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      -\n        name: checkout\n        uses: actions/checkout@master\n      -\n        name: setup go\n        uses: actions/setup-go@v1\n        with:\n          go-version: '1.13'\n      -\n        name: dependencies\n        run: |\n          go mod download\n      -\n        name: docker login\n        env:\n          DOCKER_USER: ${{ secrets.docker_user }}\n          DOCKER_PASSWORD: ${{ secrets.docker_password }}\n        run: |\n          echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin\n      -\n        name: build\n        run: |\n          export GOPATH=/home/runner/go\n          export PATH=\"$PATH:$GOPATH/bin\"\n          make build\n      -  \n        name: test\n        run: |\n          export GOPATH=/home/runner/go\n          export PATH=\"$PATH:$GOPATH/bin\"\n          make test\n          make smoke-test\n      - \n        name: release\n        uses: goreleaser/goreleaser-action@v1\n        with:\n          args: release --skip-validate\n        env:\n          GITHUB_TOKEN: ${{ secrets.gh_actions_token }}\n"
  },
  {
    "path": ".github/workflows/bump_version.yml",
    "content": "# Push with a commit message containing `#major` to bump major version\n# and update the major version number here.\n\n# MAJOR Version: 1.x\n\nname: Bump Version\n\non:\n  push:\n    branches:\n      - master\n    tags-ignore:\n      - '**'\n    paths-ignore:\n      - 'docs/**'\n      - '**.md'\n      - '.devcontainer/**'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@master\n      with:\n        fetch-depth: '0'\n    - name: Bump version and push tag\n      uses: anothrNick/github-tag-action@1.19.0\n      env:\n        GITHUB_TOKEN: ${{ secrets.gh_actions_token }}\n        WITH_V: true\n        DEFAULT_BUMP: minor\n"
  },
  {
    "path": ".github/workflows/vscode_remote_development.yml",
    "content": "name: VS Code DockerHub Build & Push\n\non:\n  push:\n    branches:\n      - 'master'\n    paths:\n      - '.devcontainer/**'\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      -\n        name: checkout\n        uses: actions/checkout@master\n      -\n        name: docker login\n        env:\n          DOCKER_USER: ${{ secrets.docker_user }}\n          DOCKER_PASSWORD: ${{ secrets.docker_password }}\n        run: |\n          echo $DOCKER_PASSWORD | docker login -u $DOCKER_USER --password-stdin\n      -\n        name: Build & Push to DockerHub\n        env:\n          DOCKER_ORG: stelligent\n        run: bash ./.devcontainer/build/dockerhub.sh\n"
  },
  {
    "path": ".gitignore",
    "content": "# Local dev\ndist/\n.bundle\n.DS_Store\n.vscode/**/*\n.release/\n.idea/\n.DS_Store\n.test/\n*/coverage.out\n*/*packr.go\n**/*.log\n**/*.pem\n**/*.retry\n**/*.sw*\n**/local.json\n\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736\n.glide/\n\n# Dependency directories (remove the comment below to include it)\nvendor/\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "builds:\n-\n  main: ./cli\n  env:\n    - CGO_ENABLED=0\n  goos:\n   - linux\n   - darwin\n   - windows\n  goarch:\n    - 386\n    - amd64\n    - arm\n    - arm64\narchives:\n  - id: archive\n    name_template: '{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}'\n    replacements:\n      darwin: Darwin\n      linux: Linux\n      windows: Windows\n      386: i386\n      amd64: x86_64\n    format_overrides:\n      - goos: windows\n        format: zip\nchecksum:\n  name_template: 'checksums.txt'\nsnapshot:\n  name_template: \"{{ .Tag }}-next\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n    - '^docs:'\n    - '^test:'\nbrews:\n  -\n    github:\n      owner: stelligent\n      name: homebrew-tap\n    commit_author:\n      name: goreleaserbot\n      email: goreleaser@stelligent.com\n    folder: Formula\n    homepage:  https://github.com/stelligent/config-lint\n    description: Validate configuration files using rules specified in YAML\n    test: |\n      system \"#{bin}/config-lint -version\"\ndockers:\n  - \n    dockerfile: .dockerhub/Dockerfile\n    image_templates:\n    - \"stelligent/config-lint:{{ .Tag }}\"\n    - \"stelligent/config-lint:latest\"\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to config-lint\n\nHelp wanted! We'd love your contributions to config-lint. Please review the following guidelines before contributing. Also, feel free to propose changes to these guidelines by updating this file and submitting a pull request.\n\n- [I have a question](#questions)\n- [I found a bug](#bugs)\n- [I have a feature request](#features)\n- [I have a contribution to share](#process)\n\n## <a name=\"questions\"></a> Have a Question?\n\nPlease don't open a GitHub issue for questions about how to use config-lint, as the goal is to use issues for managing bugs and feature requests.\n\nUntil we have a better way to communicate with users, please use the [Stelligent contact page](https://stelligent.com/contact/) if you have any questions.\n\n## <a name=\"bugs\"></a> Found a Bug?\n\nIf you've identified a bug in config-lint, please [submit an issue](#issue) to our GitHub repo:\n[stelligent/config-lint](https://github.com/stelligent/config-lint/issues/new). Please also feel free to submit a [Pull Request](#pr) with a fix for the bug!\n\n## <a name=\"features\"></a> Have a Feature Request?\n\nAll feature requests should start with [submitting an issue](#issue) documenting the user story and acceptance criteria. Again, feel free to submit a [Pull Request](#pr) with a proposed implementation of the feature.\n\n## <a name=\"process\"></a> Ready to Contribute!\n\n### <a name=\"issue\"></a> Create an issue\n\nBefore submitting a new issue, please search the issues to make sure there isn't a similar issue doesn't already exist.\n\nAssuming no existing issues exist, please ensure you include the following bits of information when submitting the issue to ensure we can quickly reproduce your issue:\n\n- The version of config-lint.\n- The platform (Linux, OS X, Windows).\n- The complete rule file(s) used if not using a built-in ruleset.\n- The complete code the rule is running against.\n- The complete command that was executed.\n- Any output from the command.\n- Details of the expected results and how they differed from the actual results.\n\nWe may have additional questions and will communicate through the GitHub issue, so please respond back to our questions to help reproduce and resolve the issue as quickly as possible.\n\nNew issues can be created with in our [GitHub\nrepo](https://github.com/stelligent/config-lint/issues/new).\n\n### <a name=\"pr\"></a>Pull Requests\n\nPull requests should target the `master` branch. Ensure you have a successful build for your branch. Please also reference the issue from the description of the pull request using [special keyword\nsyntax](https://help.github.com/articles/closing-issues-via-commit-messages/) to auto close the issue when the PR is merged. For example, include the phrase `fixes #14` in the PR description to have issue #14 auto close.\n\n### <a name=\"style\"></a> Styleguide\n\nWhen submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. \n\nHere are a few points to keep in mind:\n\n- Please run `make lint` before committing to ensure code aligns\n  with go standards.\n- Please run `make cyclo` before committing to ensure cyclomatic complexity is lower than 15.\n- Pleae ensure any new code is well tested and running `make test` is successful.\n- Dependencies are managed with [go modules](https://blog.golang.org/using-go-modules) and dependency requirements are defined in [go.mod](go.mod)\n\n### License\n\nBy contributing your code, you agree to license your contribution under the\nterms of the [MIT License](LICENSE).\n\nAll files are released with the MIT license."
  },
  {
    "path": "LICENSE.md",
    "content": "MIT License\n\nCopyright (c) 2018-2020 Stelligent\nPortions copyright 2019 Liam Galvin (https://github.com/liamg/tfsec)\n\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Makefile",
    "content": "# Versioning based on latest git tag.\nVERSION := $(shell git tag -l --sort=creatordate | grep \"^v[0-9]*.[0-9]*.[0-9]*$$\" | tail -1)\nBUILD_DIR = .release\nGOLDFLAGS = \"-X main.version=$(VERSION)\"\n\nCLI_FILES = $(shell find cli linter assertion -name \\*.go)\n\ndefault: all\n\ndevdeps:\n\t@echo \"=== dev dependencies ===\"\n\t@go get \"github.com/gobuffalo/packr/...\"\n\t@go get -u golang.org/x/lint/golint\n\t@go get \"github.com/fzipp/gocyclo\"\n\ndeps:\n\t@echo \"=== dependencies ===\"\n\tgo mod download\n\ngen:\n\t@echo \"=== generating ===\"\n\t@go get \"github.com/gobuffalo/packr/...\"\n\t@go generate ./...\n\nlint: gen\n\t@echo \"=== linting ===\"\n\t@go vet ./...\n\t@go get -u golang.org/x/lint/golint\n\t@golint $(go list ./... | grep -v /vendor/)\n\ncyclo:\n\t@echo \"=== cyclomatic complexity ===\"\n\t@go get \"github.com/fzipp/gocyclo\"\n\t@gocyclo -over 15 assertion linter cli || echo \"WARNING: cyclomatic complexity is high\"\n\ntest: lint cyclo\n\t@echo \"=== testing ===\"\n\t@go test -v ./...\n\ntesttf: lint cyclo\n\t@echo \"=== testing Terraform Built In Rules ===\"\n\t@go test -v ./cli/... -run TestTerraformBuiltInRules\n\n$(BUILD_DIR)/config-lint: $(CLI_FILES)\n\t@echo \"=== building config-lint - $@ ===\"\n\tmkdir -p $(BUILD_DIR)\n\tGOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags=$(GOLDFLAGS) -o $(BUILD_DIR)/config-lint cli/*.go\n\nbuild: gen $(BUILD_DIR)/config-lint\n\nall: clean deps test build smoke-test\ndev: deps devdeps\n\nclean:\n\t@echo \"=== cleaning ===\"\n\trm -rf $(BUILD_DIR)\n\trm -rf vendor\n\trm -f cli/*-packr.go\n\ncover-assertion:\n\t@cd assertion && go test -coverprofile=coverage.out && go tool cover -html=coverage.out\n\ncover-linter:\n\t@cd linter && go test -coverprofile=coverage.out && go tool cover -html=coverage.out\n\ncover-cli:\n\t@cd cli && go test -coverprofile=coverage.out && go tool cover -html=coverage.out\n\nsmoke-test:\n\t@$(BUILD_DIR)/config-lint -terraform cli/testdata/smoketest_tf12.tf\n\t@$(BUILD_DIR)/config-lint -tfparser tf11 -terraform cli/testdata/smoketest_tf11.tf\n\t@$(BUILD_DIR)/config-lint -tfparser tf11 -terraform -profile cli/testdata/profile-exceptions.yml cli/testdata/smoketest_exceptions.tf\n"
  },
  {
    "path": "README.md",
    "content": "[![Latest Release](https://img.shields.io/github/v/release/stelligent/config-lint?color=%233D9970)](https://img.shields.io/github/v/release/stelligent/config-lint?color=%233D9970)\n[![Build & Deploy](https://github.com/stelligent/config-lint/workflows/Build%20%26%20Deploy/badge.svg)](https://github.com/stelligent/config-lint/workflows/Build%20%26%20Deploy/badge.svg)\n[![Go Report Card](https://goreportcard.com/badge/github.com/stelligent/config-lint)](https://goreportcard.com/report/github.com/stelligent/config-lint)\n\n# 🔍 config-lint 🔎\n\nA command line tool to validate configuration files using rules specified in YAML. The configuration files can be one of several formats: Terraform, JSON, YAML, with support for Kubernetes. There are built-in rules provided for Terraform, and custom files can be used for other formats.\n\n📓 [Documentation](https://stelligent.github.io/config-lint)\n\n👷 [Contributing](CONTRIBUTING.md)\n\n🐛 [Issues & Bugs](https://github.com/stelligent/config-lint/issues)\n\n## Blog Posts\n\n✏️ [config-lint: Up and Running](https://stelligent.com/2020/04/17/config-lint-up-and-running/)\n\n\n✏️ [Development Acceleration Through VS Code Remote Containers](https://stelligent.com/2020/04/10/development-acceleration-through-vs-code-remote-containers-setting-up-a-foundational-configuration/)\n\n## Quick Start\n\nInstall the latest version of config-lint on macOS using [Homebrew](https://brew.sh/):\n\n``` bash\nbrew tap stelligent/tap\nbrew install config-lint\n```\n\nOr manually on Linux:\n\n``` bash\ncurl -L https://github.com/stelligent/config-lint/releases/latest/download/config-lint_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin config-lint\nchmod +rx /usr/local/bin/config-lint\n```\n\nRun the built-in ruleset against your Terraform files. For instance if you want to run config-lint against our [example files](example-files/):\n\n``` bash\nconfig-lint -terraform example-files/config\n```\n\nYou will see failure and warning violations in the output like this:\n``` bash\n[\n  {\n    \"AssertionMessage\": \"viewer_certificate[].cloudfront_default_certificate | [0] should be 'false', not ''\",\n    \"Category\": \"resource\",\n    \"CreatedAt\": \"2020-04-15T19:24:33Z\",\n    \"Filename\": \"example-files/config/cloudfront.tf\",\n    \"LineNumber\": 10,\n    \"ResourceID\": \"s3_distribution\",\n    \"ResourceType\": \"aws_cloudfront_distribution\",\n    \"RuleID\": \"CLOUDFRONT_MINIMUM_SSL\",\n    \"RuleMessage\": \"CloudFront Distribution must use TLS 1.2\",\n    \"Status\": \"FAILURE\"\n  },\n  ...\n```\n\nYou can find more install options in our [installation guide](/docs/install.md)."
  },
  {
    "path": "assertion/compare.go",
    "content": "package assertion\n\nimport (\n\t\"strconv\"\n\t\"time\"\n)\n\nfunc intCompare(n1 int, n2 int) int {\n\tif n1 < n2 {\n\t\treturn -1\n\t}\n\tif n1 > n2 {\n\t\treturn 1\n\t}\n\treturn 0\n}\n\nfunc daysOld(data interface{}) int {\n\tif stringValue, ok := data.(string); ok {\n\t\tlayout := \"2006-01-02T15:04:05Z\"\n\t\tt, err := time.Parse(layout, stringValue)\n\t\tif err != nil {\n\t\t\treturn 0\n\t\t}\n\t\tdays := int(time.Since(t).Hours() / 24.0)\n\t\tDebugf(\"Date: %v Days ago: %d\\n\", data, days)\n\t\treturn days\n\t}\n\treturn 0\n}\n\nfunc compare(data interface{}, value string, valueType string) int {\n\tswitch valueType {\n\tcase \"size\":\n\t\tn, _ := strconv.Atoi(value)\n\t\tl := 0\n\t\tswitch v := data.(type) {\n\t\tcase []interface{}:\n\t\t\tl = len(v)\n\t\tcase map[string]interface{}:\n\t\t\tl = len(v)\n\t\t}\n\t\treturn intCompare(l, n)\n\tcase \"integer\":\n\t\tswitch v := data.(type) {\n\t\tcase float64:\n\t\t\tn1 := int(v)\n\t\t\tn2, _ := strconv.Atoi(value)\n\t\t\treturn intCompare(n1, n2)\n\t\tcase int:\n\t\t\tn2, _ := strconv.Atoi(value)\n\t\t\treturn intCompare(v, n2)\n\t\tcase string:\n\t\t\tn1, _ := strconv.Atoi(v)\n\t\t\tn2, _ := strconv.Atoi(value)\n\t\t\treturn intCompare(n1, n2)\n\t\t}\n\t\treturn 0\n\tcase \"age\":\n\t\tn, _ := strconv.Atoi(value)\n\t\treturn intCompare(daysOld(data), n)\n\tdefault:\n\t\ttmp, _ := JSONStringify(data)\n\t\ts := unquoted(tmp)\n\t\tif s > value {\n\t\t\treturn 1\n\t\t}\n\t\tif s < value {\n\t\t\treturn -1\n\t\t}\n\t\treturn 0\n\t}\n}\n"
  },
  {
    "path": "assertion/compare_test.go",
    "content": "package assertion\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc TestDaysOldForToday(t *testing.T) {\n\tnow := time.Now().Format(\"2006-01-02T15:04:05Z\")\n\tassert.Equal(t, 0, daysOld(now), \"Expecting daysOld to return 0\")\n}\n\nfunc TestDaysOldFor90DaysAgo(t *testing.T) {\n\tthen := time.Now().Add(-time.Duration(90) * time.Hour * 24).Format(\"2006-01-02T15:04:05Z\")\n\tassert.Equal(t, 90, daysOld(then), \"Expecting daysOld to return 90\")\n}\n"
  },
  {
    "path": "assertion/contains.go",
    "content": "package assertion\n\nimport (\n\t\"strings\"\n)\n\nfunc interfaceListContains(v []interface{}, key, value string) (MatchResult, error) {\n\tfor _, element := range v {\n\t\tif stringElement, isString := element.(string); isString {\n\t\t\tif stringElement == value {\n\t\t\t\treturn matches()\n\t\t\t}\n\t\t\tif strings.Contains(stringElement, value) {\n\t\t\t\treturn matches()\n\t\t\t}\n\t\t}\n\t}\n\treturn doesNotMatch(\"%v does not contain %v\", key, value)\n}\n\nfunc stringListContains(v []string, key, value string) (MatchResult, error) {\n\tfor _, stringElement := range v {\n\t\tif stringElement == value {\n\t\t\treturn matches()\n\t\t}\n\t\tif strings.Contains(stringElement, value) {\n\t\t\treturn matches()\n\t\t}\n\t}\n\treturn doesNotMatch(\"%v does not contain %v\", key, value)\n}\n\nfunc stringContains(v string, key, value string) (MatchResult, error) {\n\tif strings.Contains(v, value) {\n\t\treturn matches()\n\t}\n\treturn doesNotMatch(\"%v does not contain %v\", key, value)\n}\n\nfunc defaultContains(data interface{}, key, value string) (MatchResult, error) {\n\tsearchResult, err := JSONStringify(data)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tif strings.Contains(searchResult, value) {\n\t\treturn matches()\n\t}\n\treturn doesNotMatch(\"%v does not contain %v\", key, value)\n}\n\nfunc contains(data interface{}, key, value string) (MatchResult, error) {\n\tswitch v := data.(type) {\n\tcase []interface{}:\n\t\treturn interfaceListContains(v, key, value)\n\tcase []string:\n\t\treturn stringListContains(v, key, value)\n\tcase string:\n\t\treturn stringContains(v, key, value)\n\tdefault:\n\t\treturn defaultContains(v, key, value)\n\t}\n}\n\nfunc doesNotContain(data interface{}, key, value string) (MatchResult, error) {\n\tm, err := contains(data, key, value)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tif m.Match {\n\t\treturn doesNotMatch(\"%v should not contain %v\", key, value)\n\t}\n\treturn matches()\n}\n\nfunc startsWith(data interface{}, key, prefix string) (MatchResult, error) {\n\tswitch v := data.(type) {\n\tcase string:\n\t\tif strings.HasPrefix(v, prefix) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v does not start with %v\", key, prefix)\n\tdefault:\n\t\treturn doesNotMatch(\"%v is not a string %v\", key, prefix)\n\t}\n}\n\nfunc endsWith(data interface{}, key, suffix string) (MatchResult, error) {\n\tswitch v := data.(type) {\n\tcase string:\n\t\tif strings.HasSuffix(v, suffix) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v does not end with %v\", key, suffix)\n\tdefault:\n\t\treturn doesNotMatch(\"%v is not a string %v\", key, suffix)\n\t}\n}\n"
  },
  {
    "path": "assertion/contains_test.go",
    "content": "package assertion\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\n// The non error cases are covered in match_test\n\nfunc TestContainsWithNonJSONType(t *testing.T) {\n\tvar complexNumber complex128\n\t_, err := contains(complexNumber, \"foo\", \"1\")\n\tif err == nil {\n\t\tt.Errorf(\"Expecting contains to return an error for non JSON encodable data\")\n\t}\n}\n\nfunc TestDoesNotContainWithNonJSONType(t *testing.T) {\n\tvar complexNumber complex128\n\t_, err := doesNotContain(complexNumber, \"foo\", \"1\")\n\tif err == nil {\n\t\tt.Errorf(\"Expecting doesNotContain to return an error for non JSON encodable data\")\n\t}\n}\n\nfunc TestContainsWithString(t *testing.T) {\n\ts := \"s3:Get*\"\n\tmatch, err := contains(s, \"Action\", \"*\")\n\tassert.Nil(t, err, \"Expecting no error from contains\")\n\tassert.True(t, match.Match, \"Expecting match for string\")\n}\n\nfunc TestContainsWithSliceOfStrings(t *testing.T) {\n\ts := []string{\"s3:Get*\"}\n\tmatch, err := contains(s, \"Action\", \"*\")\n\tassert.Nil(t, err, \"Expecting no error from contains\")\n\tassert.True(t, match.Match, \"Expecting match for string\")\n}\n"
  },
  {
    "path": "assertion/expression.go",
    "content": "package assertion\n\nfunc searchAndMatch(expression Expression, resource Resource) (MatchResult, error) {\n\tv, err := SearchData(expression.Key, resource.Properties)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tmatch, err := isMatch(v, expression)\n\tDebugf(\"ResourceID: %s Type: %s %v\\n\",\n\t\tresource.ID,\n\t\tresource.Type,\n\t\tmatch)\n\treturn match, err\n}\n\nfunc orExpression(expressions []Expression, resource Resource) (MatchResult, error) {\n\tfor _, childExpression := range expressions {\n\t\tmatch, err := booleanExpression(childExpression, resource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif match.Match {\n\t\t\treturn matches()\n\t\t}\n\t}\n\treturn doesNotMatch(\"Or expression fails\") // TODO needs more information\n}\n\nfunc xorExpression(expressions []Expression, resource Resource) (MatchResult, error) {\n\tmatchCount := 0\n\tfor _, childExpression := range expressions {\n\t\tmatch, err := booleanExpression(childExpression, resource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif match.Match {\n\t\t\tmatchCount++\n\t\t}\n\t}\n\tif matchCount == 1 {\n\t\treturn matches()\n\t}\n\treturn doesNotMatch(\"Xor expression fails\") // TODO needs more information\n}\n\nfunc andExpression(expressions []Expression, resource Resource) (MatchResult, error) {\n\tfor _, childExpression := range expressions {\n\t\tmatch, err := booleanExpression(childExpression, resource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif !match.Match {\n\t\t\treturn doesNotMatch(\"And expression fails: %s\", match.Message)\n\t\t}\n\t}\n\treturn matches()\n}\n\nfunc notExpression(expressions []Expression, resource Resource) (MatchResult, error) {\n\t// more than one child filter treated as not any\n\tfor _, childExpression := range expressions {\n\t\tmatch, err := booleanExpression(childExpression, resource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif match.Match {\n\t\t\treturn doesNotMatch(\"Not expression fails\") // TODO needs more information\n\t\t}\n\t}\n\treturn matches()\n}\n\nfunc collectResources(key string, resource Resource) ([]Resource, error) {\n\tresources := make([]Resource, 0)\n\tvalue, err := SearchData(key, resource.Properties)\n\tif err != nil {\n\t\treturn resources, err\n\t}\n\tif collection, ok := value.([]interface{}); ok {\n\t\tfor _, properties := range collection {\n\t\t\tcollectionResource := Resource{\n\t\t\t\tID:         resource.ID,\n\t\t\t\tType:       resource.Type,\n\t\t\t\tProperties: properties,\n\t\t\t\tFilename:   resource.Filename,\n\t\t\t}\n\t\t\tresources = append(resources, collectionResource)\n\t\t}\n\t}\n\treturn resources, nil\n}\n\nfunc everyExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {\n\tresources, err := collectResources(collectionExpression.Key, resource)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tfor _, collectionResource := range resources {\n\t\tmatch, err := andExpression(collectionExpression.Expressions, collectionResource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif !match.Match {\n\t\t\t// at least one element is false, so entire expression is false\n\t\t\treturn doesNotMatch(\"Every expression fails: %s\", match.Message)\n\t\t}\n\t}\n\t// every element passes, so entire expression is true\n\treturn matches()\n}\n\nfunc someExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {\n\tresources, err := collectResources(collectionExpression.Key, resource)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tfor _, collectionResource := range resources {\n\t\tmatch, err := andExpression(collectionExpression.Expressions, collectionResource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\t// at least one element passes, so entire expression is true\n\t\tif match.Match {\n\t\t\treturn matches()\n\t\t}\n\t}\n\t// no element passes, so entire expression is false\n\treturn doesNotMatch(\"Some expression fails\") // TODO needs more information\n}\n\nfunc noneExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {\n\tresources, err := collectResources(collectionExpression.Key, resource)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tfor _, collectionResource := range resources {\n\t\tmatch, err := andExpression(collectionExpression.Expressions, collectionResource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\t// at least one element passes, so entire expression is false\n\t\tif match.Match {\n\t\t\treturn doesNotMatch(\"None expression fails: %s\", match.Message)\n\t\t}\n\t}\n\t// no element passes, so entire expression is true\n\treturn matches()\n}\n\nfunc exactlyOneExpression(collectionExpression CollectionExpression, resource Resource) (MatchResult, error) {\n\tresources, err := collectResources(collectionExpression.Key, resource)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tmatchCount := 0\n\tfor _, collectionResource := range resources {\n\t\tmatch, err := andExpression(collectionExpression.Expressions, collectionResource)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif match.Match {\n\t\t\tmatchCount++\n\t\t}\n\t}\n\tif matchCount == 1 {\n\t\treturn matches()\n\t}\n\treturn doesNotMatch(\"ExactlyOne expression fails\")\n}\n\nfunc booleanExpression(expression Expression, resource Resource) (MatchResult, error) {\n\tif expression.Or != nil && len(expression.Or) > 0 {\n\t\treturn orExpression(expression.Or, resource)\n\t}\n\tif expression.Xor != nil && len(expression.Xor) > 0 {\n\t\treturn xorExpression(expression.Xor, resource)\n\t}\n\tif expression.And != nil && len(expression.And) > 0 {\n\t\treturn andExpression(expression.And, resource)\n\t}\n\tif expression.Not != nil && len(expression.Not) > 0 {\n\t\treturn notExpression(expression.Not, resource)\n\t}\n\tif expression.Every.Key != \"\" {\n\t\treturn everyExpression(expression.Every, resource)\n\t}\n\tif expression.Some.Key != \"\" {\n\t\treturn someExpression(expression.Some, resource)\n\t}\n\tif expression.None.Key != \"\" {\n\t\treturn noneExpression(expression.None, resource)\n\t}\n\tif expression.ExactlyOne.Key != \"\" {\n\t\treturn exactlyOneExpression(expression.ExactlyOne, resource)\n\t}\n\treturn searchAndMatch(expression, resource)\n}\n\n// ExcludeResource when resource.ID included in list of exceptions\nfunc ExcludeResource(rule Rule, resource Resource) bool {\n\tfor _, id := range rule.Except {\n\t\tif id == resource.ID {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// FilterResourceExceptions filters out resources that should not be validated\nfunc FilterResourceExceptions(rule Rule, resources []Resource) []Resource {\n\tif rule.Except == nil || len(rule.Except) == 0 {\n\t\treturn resources\n\t}\n\tfiltered := make([]Resource, 0)\n\tfor _, resource := range resources {\n\t\tif ExcludeResource(rule, resource) {\n\t\t\tfiltered = append(filtered, resource)\n\t\t}\n\t}\n\treturn filtered\n}\n\n// CheckExpression validates a single Resource using a single Expression\nfunc CheckExpression(rule Rule, expression Expression, resource Resource) (Result, error) {\n\tresult := Result{\n\t\tStatus:  \"OK\",\n\t\tMessage: \"\",\n\t}\n\tmatch, err := booleanExpression(expression, resource)\n\tif err != nil {\n\t\tDebugJSON(\"Error: \", err)\n\t\tresult.Status = \"FAILURE\"\n\t\tresult.Message = err.Error()\n\t\treturn result, err\n\t}\n\tif !match.Match {\n\t\tif rule.Severity == \"\" {\n\t\t\tresult.Status = \"FAILURE\"\n\t\t} else {\n\t\t\tresult.Status = rule.Severity\n\t\t}\n\t\tresult.Message = match.Message\n\t}\n\treturn result, nil\n}\n"
  },
  {
    "path": "assertion/expression_test.go",
    "content": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n)\n\ntype ExpressionTestCase struct {\n\tRule           Rule\n\tResource       Resource\n\tExpectedStatus string\n}\n\nfunc TestCheckExpression(t *testing.T) {\n\n\tsimpleTestResource := Resource{\n\t\tID:   \"a_test_resource\",\n\t\tType: \"aws_instance\",\n\t\tProperties: map[string]interface{}{\n\t\t\t\"instance_type\": \"t2.micro\",\n\t\t\t\"ami\":           \"ami-f2d3638a\",\n\t\t},\n\t\tFilename: \"test.tf\",\n\t}\n\tresourceWithTags := Resource{\n\t\tID:   \"another_test_resource\",\n\t\tType: \"aws_instance\",\n\t\tProperties: map[string]interface{}{\n\t\t\t\"instance_type\": \"t2.micro\",\n\t\t\t\"ami\":           \"ami-f2d3638a\",\n\t\t\t\"tags\": map[string]interface{}{\n\t\t\t\t\"Environment\": \"Development\",\n\t\t\t\t\"Project\":     \"Web\",\n\t\t\t},\n\t\t},\n\t\tFilename: \"test.tf\",\n\t}\n\tresourceWithRootVolume := Resource{\n\t\tID:   \"another_test_resource\",\n\t\tType: \"aws_instance\",\n\t\tProperties: map[string]interface{}{\n\t\t\t\"instance_type\": \"t2.micro\",\n\t\t\t\"ami\":           \"ami-f2d3638a\",\n\t\t\t\"root_block_device\": map[string]interface{}{\n\t\t\t\t\"volume_size\": \"1000\",\n\t\t\t},\n\t\t},\n\t\tFilename: \"test.tf\",\n\t}\n\n\ttestCases := map[string]ExpressionTestCase{\n\t\t\"testEq\": {\n\t\t\tRule{\n\t\t\t\tID:       \"test1\",\n\t\t\t\tMessage:  \"test rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testOr\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tOr: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"m4.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testOrFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tOr: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.nano\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"m4.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testXor\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tXor: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"m4.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testXorFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tXor: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testXorFailsAgain\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tXor: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"m3.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"c4.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testAnd\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tAnd: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"ami\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"ami-f2d3638a\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testAndFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tAnd: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"m3.medium\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"ami\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"ami-f2d3638a\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testNot\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tNot: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"c4.large\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testNotFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tNot: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testNestedNot\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TEST1\",\n\t\t\t\tMessage:  \"Test Rule\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tNot: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tOr: []Expression{\n\t\t\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\t\t\tValue: \"t2.micro\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\t\t\tKey:   \"instance_type\",\n\t\t\t\t\t\t\t\t\t\tOp:    \"eq\",\n\t\t\t\t\t\t\t\t\t\tValue: \"m3.medium\",\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tsimpleTestResource,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testSizeFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TESTCOUNT\",\n\t\t\t\tMessage:  \"Test Resource Count Fails\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tKey:       \"tags\",\n\t\t\t\t\t\tValueType: \"size\",\n\t\t\t\t\t\tOp:        \"eq\",\n\t\t\t\t\t\tValue:     \"3\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresourceWithTags,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testSizeOK\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TESTCOUNT\",\n\t\t\t\tMessage:  \"Test Resource Count OK\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tKey:       \"tags\",\n\t\t\t\t\t\tValueType: \"size\",\n\t\t\t\t\t\tOp:        \"eq\",\n\t\t\t\t\t\tValue:     \"2\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresourceWithTags,\n\t\t\t\"OK\",\n\t\t},\n\t\t\"testIntegerFails\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TESTCOUNT\",\n\t\t\t\tMessage:  \"Test integer Fails\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tKey:       \"root_block_device.volume_size\",\n\t\t\t\t\t\tValueType: \"integer\",\n\t\t\t\t\t\tOp:        \"le\",\n\t\t\t\t\t\tValue:     \"500\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresourceWithRootVolume,\n\t\t\t\"FAILURE\",\n\t\t},\n\t\t\"testIntegerOK\": {\n\t\t\tRule{\n\t\t\t\tID:       \"TESTCOUNT\",\n\t\t\t\tMessage:  \"Test integer OK\",\n\t\t\t\tSeverity: \"FAILURE\",\n\t\t\t\tResource: \"aws_instance\",\n\t\t\t\tAssertions: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tKey:       \"root_block_device.volume_size\",\n\t\t\t\t\t\tValueType: \"integer\",\n\t\t\t\t\t\tOp:        \"le\",\n\t\t\t\t\t\tValue:     \"2000\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t\tresourceWithRootVolume,\n\t\t\t\"OK\",\n\t\t},\n\t}\n\n\tfor k, tc := range testCases {\n\t\texpressionResult, err := CheckExpression(tc.Rule, tc.Rule.Assertions[0], tc.Resource)\n\t\tFailTestIfError(err, \"TestSimple\", t)\n\t\tif expressionResult.Status != tc.ExpectedStatus {\n\t\t\tt.Errorf(\"%s Failed Expected '%s' to be '%s'\", k, expressionResult.Status, tc.ExpectedStatus)\n\t\t}\n\t}\n}\n\nfunc TestNestedBooleans(t *testing.T) {\n\trule := Rule{\n\t\tID:       \"TEST1\",\n\t\tMessage:  \"Do not allow access to port 22 from 0.0.0.0/0\",\n\t\tSeverity: \"NOT_COMPLIANT\",\n\t\tResource: \"aws_instance\",\n\t\tAssertions: []Expression{\n\t\t\tExpression{\n\t\t\t\tNot: []Expression{\n\t\t\t\t\tExpression{\n\t\t\t\t\t\tAnd: []Expression{\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"ipPermissions[].fromPort[]\",\n\t\t\t\t\t\t\t\tOp:    \"contains\",\n\t\t\t\t\t\t\t\tValue: \"22\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tExpression{\n\t\t\t\t\t\t\t\tKey:   \"ipPermissions[].ipRanges[]\",\n\t\t\t\t\t\t\t\tOp:    \"contains\",\n\t\t\t\t\t\t\t\tValue: \"0.0.0.0/0\",\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{},\n\t\tFilename:   \"test.tf\",\n\t}\n\tresourceJSON := `{\n            \"description\": \"2017-12-03T03:14:29.856Z\",\n            \"groupName\": \"test-8246\",\n            \"ipPermissions\": [\n                {\n                    \"fromPort\": \"22\",\n                    \"ipProtocol\": \"tcp\",\n                    \"toPort\": \"22\",\n                    \"ipv4Ranges\": [\n                        {\n                            \"cidrIp\": \"0.0.0.0/0\"\n                        }\n                    ],\n                    \"ipRanges\": [\n                        \"0.0.0.0/0\"\n                    ]\n                }\n            ]\n        }`\n\terr := json.Unmarshal([]byte(resourceJSON), &resource.Properties)\n\tif err != nil {\n\t\tt.Error(\"Error parsing resource JSON\")\n\t}\n\texpressionResult, err := CheckExpression(rule, rule.Assertions[0], resource)\n\tFailTestIfError(err, \"TestNestedBoolean\", t)\n\tif expressionResult.Status != \"NOT_COMPLIANT\" {\n\t\tt.Error(\"Expecting nested boolean to return NOT_COMPLIANT\")\n\t}\n}\n\nfunc TestExceptions(t *testing.T) {\n\trule := Rule{\n\t\tID:     \"EXCEPT\",\n\t\tExcept: []string{\"200\", \"300\"},\n\t}\n\tresources := []Resource{\n\t\tResource{ID: \"100\"},\n\t\tResource{ID: \"200\"},\n\t\tResource{ID: \"300\"},\n\t\tResource{ID: \"400\"},\n\t}\n\tfilteredResources := FilterResourceExceptions(rule, resources)\n\tif len(filteredResources) != 2 {\n\t\tt.Error(\"Expecting exceptions to be removed from resource list\")\n\t}\n}\n\nfunc TestNoExceptions(t *testing.T) {\n\trule := Rule{\n\t\tID:     \"EXCEPT\",\n\t\tExcept: []string{},\n\t}\n\tresources := []Resource{\n\t\tResource{ID: \"100\"},\n\t\tResource{ID: \"200\"},\n\t\tResource{ID: \"300\"},\n\t\tResource{ID: \"400\"},\n\t}\n\tfilteredResources := FilterResourceExceptions(rule, resources)\n\tif len(filteredResources) != 4 {\n\t\tt.Error(\"Expecting no exceptions to return all resources\")\n\t}\n}\n\nfunc TestUsingFixtures(t *testing.T) {\n\tfixtureFilenames := []string{\n\t\t\"./testdata/collection-assertions.yaml\",\n\t\t\"./testdata/has-properties.yaml\",\n\t\t\"./testdata/conditions.yaml\",\n\t\t\"./testdata/default-severity.yaml\",\n\t}\n\n\tfor _, filename := range fixtureFilenames {\n\t\tRunTestCasesFromFixture(filename, t)\n\t}\n}\n"
  },
  {
    "path": "assertion/has_properties.go",
    "content": "package assertion\n\nimport (\n\t\"strings\"\n)\n\nfunc hasProperties(data interface{}, list string) (MatchResult, error) {\n\tfor _, key := range strings.Split(list, \",\") {\n\t\tif m, ok := data.(map[string]interface{}); ok {\n\t\t\tif _, ok := m[key]; !ok {\n\t\t\t\treturn doesNotMatch(\"should have property %v\", key)\n\t\t\t}\n\t\t}\n\t}\n\treturn matches()\n}\n"
  },
  {
    "path": "assertion/helper_test.go",
    "content": "package assertion\n\nimport (\n\t\"github.com/ghodss/yaml\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\ntype (\n\t// FixtureTestCases is used to read a set of test cases from a YAML file\n\tFixtureTestCases struct {\n\t\tDescription string\n\t\tTestCases   []FixtureTestCase `json:\"test_cases\"`\n\t}\n\n\t// FixtureTestCase describes a single test case\n\tFixtureTestCase struct {\n\t\tName     string\n\t\tRule     Rule\n\t\tResource Resource\n\t\tResult   string\n\t}\n)\n\n// FailTestIfError is a helper to check err and call test Error if it is not nil\nfunc FailTestIfError(err error, message string, t *testing.T) {\n\tif err != nil {\n\t\tt.Error(message + \":\" + err.Error())\n\t}\n}\n\n// LoadTestCasesFromFixture reads YAML data describing test cases\nfunc LoadTestCasesFromFixture(filename string, t *testing.T) FixtureTestCases {\n\tvar testCases FixtureTestCases\n\tcontent, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tt.Errorf(\"Unable to read fixture file: %s\", filename)\n\t\treturn testCases\n\t}\n\terr = yaml.Unmarshal(content, &testCases)\n\tif err != nil {\n\t\tt.Errorf(\"Unable to parse fixture file: %s\", filename)\n\t\treturn testCases\n\t}\n\treturn testCases\n}\n\n// RunTestCasesFromFixture loads a YAML file describing test cases and runs them\nfunc RunTestCasesFromFixture(filename string, t *testing.T) {\n\tfixture := LoadTestCasesFromFixture(filename, t)\n\tfor _, testCase := range fixture.TestCases {\n\t\tstatus, _, err := CheckRule(testCase.Rule, testCase.Resource, mockExternalRuleInvoker())\n\t\tFailTestIfError(err, testCase.Name, t)\n\t\tif status != testCase.Result {\n\t\t\tt.Errorf(\"Test case %s returned %s expecting %s\", testCase.Name, status, testCase.Result)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "assertion/invoke.go",
    "content": "package assertion\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n)\n\n// InvokeViolation has message describing a single validation error\ntype InvokeViolation struct {\n\tMessage string\n}\n\n// InvokeResponse contains a collection of validation errors\ntype InvokeResponse struct {\n\tViolations []InvokeViolation\n}\n\n// StandardExternalRuleInvoker implements an external HTTP or HTTPS call\ntype StandardExternalRuleInvoker struct {\n}\n\nfunc makeViolation(rule Rule, resource Resource, message string) Violation {\n\treturn Violation{\n\t\tRuleID:           rule.ID,\n\t\tStatus:           rule.Severity,\n\t\tResourceID:       resource.ID,\n\t\tResourceType:     resource.Type,\n\t\tCategory:         resource.Category,\n\t\tFilename:         resource.Filename,\n\t\tRuleMessage:      rule.Message,\n\t\tAssertionMessage: message,\n\t\tCreatedAt:        currentTime(),\n\t}\n}\n\nfunc makeViolations(rule Rule, resource Resource, message string) []Violation {\n\tv := makeViolation(rule, resource, message)\n\treturn []Violation{v}\n}\n\n// Invoke an external API to validate a Resource\nfunc (e StandardExternalRuleInvoker) Invoke(rule Rule, resource Resource) (string, []Violation, error) {\n\tstatus := \"OK\"\n\tviolations := make([]Violation, 0)\n\tvar payload interface{}\n\tpayload = resource\n\tif rule.Invoke.Payload != \"\" {\n\t\tp, err := SearchData(rule.Invoke.Payload, resource.Properties)\n\t\tif err != nil {\n\t\t\treturn status, violations, err\n\t\t}\n\t\tpayload = p\n\t}\n\tpayloadJSON, err := JSONStringify(payload)\n\tif err != nil {\n\t\tviolations := makeViolations(rule, resource, fmt.Sprintf(\"Unable to create JSON payload: %s\", err.Error()))\n\t\treturn rule.Severity, violations, err\n\t}\n\tDebugf(\"Invoke %s on %s\\n\", rule.Invoke.URL, payloadJSON)\n\thttpResponse, err := http.Post(rule.Invoke.URL, \"application/json\", bytes.NewBuffer([]byte(payloadJSON)))\n\tif err != nil {\n\t\tviolations := makeViolations(rule, resource, fmt.Sprintf(\"Invoke failed: %s\", err.Error()))\n\t\treturn rule.Severity, violations, err\n\t}\n\tif httpResponse.StatusCode != 200 {\n\t\tviolations := makeViolations(rule, resource, fmt.Sprintf(\"Invoke failed, StatusCode: %d\", httpResponse.StatusCode))\n\t\treturn rule.Severity, violations, nil\n\t}\n\tdefer httpResponse.Body.Close()\n\tbody, err := ioutil.ReadAll(httpResponse.Body)\n\tif err != nil {\n\t\tviolations := makeViolations(rule, resource, \"Invoke response cannot be read\")\n\t\treturn rule.Severity, violations, nil\n\t}\n\tDebugf(\"Invoke body: %s\\n\", string(body))\n\tvar invokeResponse InvokeResponse\n\terr = json.Unmarshal(body, &invokeResponse)\n\tif err != nil {\n\t\tviolations := makeViolations(rule, resource, \"Invoke response cannot be parsed\")\n\t\treturn rule.Severity, violations, nil\n\t}\n\tfor _, violation := range invokeResponse.Violations {\n\t\tstatus = rule.Severity\n\t\tv := makeViolation(rule, resource, violation.Message)\n\t\tviolations = append(violations, v)\n\t}\n\treturn status, violations, nil\n}\n"
  },
  {
    "path": "assertion/invoke_test.go",
    "content": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestInvokeOK(t *testing.T) {\n\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintln(w, \"{}\")\n\t}))\n\tdefer ts.Close()\n\n\ti := StandardExternalRuleInvoker{}\n\trule := Rule{\n\t\tInvoke: InvokeRuleAPI{\n\t\t\tURL: ts.URL,\n\t\t},\n\t}\n\tresource := Resource{}\n\tstatus, violations, err := i.Invoke(rule, resource)\n\tassert.Equal(t, \"OK\", status, \"Expecting Invoke to return 'OK'\")\n\tassert.Equal(t, 0, len(violations), \"Expecting Invoke to return no violations\")\n\tassert.Nil(t, err, \"Expecting Invoke to not return an error\")\n}\n\nfunc TestInvokeWithViolations(t *testing.T) {\n\tresponse := InvokeResponse{\n\t\tViolations: []InvokeViolation{\n\t\t\tInvokeViolation{Message: \"Something is not right\"},\n\t\t},\n\t}\n\tjsonData, err := json.Marshal(response)\n\tassert.Nil(t, err, \"Failed to marshal test response\")\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintln(w, string(jsonData))\n\t}))\n\tdefer ts.Close()\n\n\ti := StandardExternalRuleInvoker{}\n\trule := Rule{\n\t\tSeverity: \"FAILURE\",\n\t\tInvoke: InvokeRuleAPI{\n\t\t\tURL: ts.URL,\n\t\t},\n\t}\n\tresource := Resource{}\n\tstatus, violations, err := i.Invoke(rule, resource)\n\tassert.Equal(t, \"FAILURE\", status, \"Expecting Invoke to return 'FAILURE'\")\n\tassert.Equal(t, 1, len(violations), \"Expecting Invoke to return 1 violation\")\n\tassert.Nil(t, err, \"Expecting Invoke to not return an error\")\n}\n\nfunc TestInvokeSendsMetadata(t *testing.T) {\n\n\tvar invokedResource Resource\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tbody, _ := ioutil.ReadAll(r.Body)\n\t\t_ = json.Unmarshal(body, &invokedResource)\n\t\tfmt.Fprintln(w, \"{}\")\n\t}))\n\tdefer ts.Close()\n\n\ti := StandardExternalRuleInvoker{}\n\trule := Rule{\n\t\tInvoke: InvokeRuleAPI{\n\t\t\tURL: ts.URL,\n\t\t},\n\t}\n\tresource := Resource{\n\t\tFilename: \"example.tf\",\n\t}\n\ti.Invoke(rule, resource)\n\tassert.Equal(t, resource.Filename, invokedResource.Filename, \"Expecting filename metadata in request body\")\n}\n"
  },
  {
    "path": "assertion/ip_operations.go",
    "content": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar rfc1918PrivateCIDRs = []string{\"10.0.0.0/8\", \"172.16.0.0/12\", \"192.168.0.0/16\"}\n\nfunc getIPObject(addressString string) (net.IP, error) {\n\tif !strings.Contains(addressString, \"/\") {\n\t\taddressString = fmt.Sprintf(\"%s/32\", addressString)\n\t}\n\tipAddress, _, err := net.ParseCIDR(addressString)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ipAddress, nil\n}\n\nfunc isSubnet(ipAddressStr string, supernet string) bool {\n\tipAddress, parseError := getIPObject(ipAddressStr)\n\tif parseError != nil {\n\t\tDebugf(\"%v\", parseError)\n\t\treturn false\n\t}\n\t_, superNetwork, err := net.ParseCIDR(supernet)\n\tif err != nil {\n\t\tDebugf(\"error parsing supernet: %v\", err)\n\t}\n\treturn superNetwork.Contains(ipAddress)\n}\n\nfunc isPrivateIP(ipAddressStr string) bool {\n\tfor _, cidr := range rfc1918PrivateCIDRs {\n\t\tif isSubnet(ipAddressStr, cidr) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc maxHostCount(ruleCidr string, hostLimitStr string) bool {\n\tif !strings.Contains(ruleCidr, \"/\") {\n\t\truleCidr = fmt.Sprintf(\"%s/32\", ruleCidr)\n\t}\n\thostLimit, convErr := strconv.Atoi(hostLimitStr)\n\tif convErr != nil {\n\t\tDebugf(\"error converting %v to int\", hostLimitStr)\n\t\thostLimit = 0\n\t}\n\t_, network, err := net.ParseCIDR(ruleCidr)\n\tif err != nil {\n\t\tDebugf(\"error parsing ruleCidr: %v\", ruleCidr)\n\t\treturn false\n\t}\n\tnetmaskOnes, _ := network.Mask.Size()\n\treturn hostCountByNetmaskOnes(netmaskOnes) <= hostLimit\n}\n\nfunc hostCountByNetmaskOnes(netmaskOnes int) int {\n\treturn int(math.Pow(float64(2), float64(32-netmaskOnes)))\n}\n"
  },
  {
    "path": "assertion/ip_operations_test.go",
    "content": "package assertion\n\nimport (\n\t\"testing\"\n)\n\nvar ipTests = []struct {\n\tvalue          string\n\tsupernet       string\n\texpectedResult bool\n}{\n\t{\"1.1.1.1\", \"10.0.0.0/8\", false},\n\t{\"1.1.1.1/32\", \"10.0.0.0/8\", false},\n\t{\"10.1.0.0/16\", \"10.0.0.0/8\", true},\n\t{\"10.1.1.1/32\", \"10.0.0.0/8\", true},\n\t{\"10.1.1.1\", \"10.0.0.0/8\", true},\n}\n\nfunc TestIsSubnet(t *testing.T) {\n\tfor _, input := range ipTests {\n\t\tt.Run(input.value, func(t *testing.T) {\n\t\t\tresult := isSubnet(input.value, input.supernet)\n\t\t\tif result != input.expectedResult {\n\t\t\t\tt.Errorf(\"got %v, want %v\", result, input.expectedResult)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar privateIPTests = []struct {\n\tvalue          string\n\texpectedResult bool\n}{\n\t{\"1.1.1.1\", false},\n\t{\"1.1.1.1/32\", false},\n\t{\"10.1.0.0/16\", true},\n\t{\"10.1.1.1/32\", true},\n\t{\"10.1.1.1\", true},\n\t{\"172.16.0.0/12\", true},\n\t{\"172.0.0.0/8\", false},\n\t{\"172.16.1.1\", true},\n\t{\"172.15.1.1\", false},\n\t{\"192.168.1.1\", true},\n\t{\"52.1.1.1\", false},\n\t{\"sg-1234567\", false},\n}\n\nfunc TestIsPrivateIp(t *testing.T) {\n\tfor _, input := range privateIPTests {\n\t\tt.Run(input.value, func(t *testing.T) {\n\t\t\tresult := isPrivateIP(input.value)\n\t\t\tif result != input.expectedResult {\n\t\t\t\tt.Errorf(\"got %v, want %v\", result, input.expectedResult)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar maxHostCountTests = []struct {\n\tvalue          string\n\tmax            string\n\texpectedResult bool\n}{\n\t{\"10.0.0.0/8\", \"1000\", false},\n\t{\"10.0.0.0/23\", \"500\", false},\n\t{\"10.1.0.0/16\", \"65600\", true},\n\t{\"10.1.1.1/32\", \"2\", true},\n\t{\"10.1.1.1/32\", \"1\", true},\n\t{\"10.1.1.1\", \"1\", true},\n\t{\"sg-1234567\", \"0\", false},\n}\n\nfunc TestMaxHostCount(t *testing.T) {\n\tfor _, input := range maxHostCountTests {\n\t\tt.Run(input.value, func(t *testing.T) {\n\t\t\tresult := maxHostCount(input.value, input.max)\n\t\t\tif result != input.expectedResult {\n\t\t\t\tt.Errorf(\"got %v, want %v\", result, input.expectedResult)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "assertion/log.go",
    "content": "package assertion\n\nimport \"fmt\"\n\nvar (\n\tisDebug = false\n)\n\n// SetDebug turns verbose logging on or off\nfunc SetDebug(b bool) {\n\tisDebug = b\n}\n\n// Debugf prints a formatted string when verbose logging is turned on\nfunc Debugf(format string, args ...interface{}) {\n\tif isDebug == false {\n\t\treturn\n\t}\n\tfmt.Printf(format, args...)\n}\n\nfunc DebugJSON(title string, object interface{}) {\n\tif isDebug == false {\n\t\treturn\n\t}\n\ts, _ := JSONStringify(object)\n\tfmt.Println(title)\n\tfmt.Println(s)\n}\n"
  },
  {
    "path": "assertion/match.go",
    "content": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"regexp\"\n\t\"strings\"\n)\n\nfunc matches() (MatchResult, error) {\n\treturn MatchResult{Match: true, Message: \"\"}, nil\n}\n\nfunc doesNotMatch(format string, args ...interface{}) (MatchResult, error) {\n\treturn MatchResult{\n\t\tMatch:   false,\n\t\tMessage: fmt.Sprintf(format, args...),\n\t}, nil\n}\n\nfunc matchError(err error) (MatchResult, error) {\n\treturn MatchResult{\n\t\tMatch:   false,\n\t\tMessage: err.Error(),\n\t}, err\n}\n\nfunc isMatch(data interface{}, expression Expression) (MatchResult, error) {\n\t// FIXME eliminate searchResult this when all operations converted to use data\n\t// individual ops can call JSONStringify as needed\n\tsearchResult, err := JSONStringify(data)\n\tif err != nil {\n\t\treturn matchError(err)\n\t}\n\tsearchResult = unquoted(searchResult)\n\tkey := expression.Key\n\top := expression.Op\n\tvalue := expression.Value\n\tvalueType := expression.ValueType\n\n\tswitch op {\n\tcase \"eq\":\n\t\tif compare(data, value, valueType) == 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be equal to %v\", key, searchResult, value)\n\tcase \"ne\":\n\t\tif compare(data, value, valueType) != 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should not be equal to %v\", key, searchResult, value)\n\tcase \"lt\":\n\t\tif compare(data, value, valueType) < 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be less than %v\", key, searchResult, value)\n\tcase \"le\":\n\t\tif compare(data, value, valueType) <= 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be less than or equal to %v\", key, searchResult, value)\n\tcase \"gt\":\n\t\tif compare(data, value, valueType) > 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be greater than %v\", key, searchResult, value)\n\tcase \"ge\":\n\t\tif compare(data, value, valueType) >= 0 {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be greater than or equal to %v\", key, searchResult, value)\n\tcase \"in\":\n\t\tfor _, v := range strings.Split(value, \",\") {\n\t\t\tif v == searchResult {\n\t\t\t\treturn matches()\n\t\t\t}\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should be in %v\", key, searchResult, value)\n\tcase \"not-in\":\n\t\tfor _, v := range strings.Split(value, \",\") {\n\t\t\tif v == searchResult {\n\t\t\t\treturn doesNotMatch(\"%v(%v) should not be in %v\", key, searchResult, value)\n\t\t\t}\n\t\t}\n\t\treturn matches()\n\tcase \"absent\":\n\t\tif isAbsent(searchResult) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be absent\", key)\n\tcase \"present\":\n\t\tif isPresent(searchResult) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be present\", key)\n\tcase \"null\":\n\t\tif data == nil {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be null\", key)\n\tcase \"not-null\":\n\t\tif data != nil {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should not be null\", key)\n\tcase \"empty\":\n\t\tif isEmpty(data) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be empty\", key)\n\tcase \"not-empty\":\n\t\tif !isEmpty(data) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should not be empty\", key)\n\tcase \"is-array\":\n\t\tif isArray(data) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be an array\", key)\n\tcase \"is-not-array\":\n\t\tif !isArray(data) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should not be an array\", key)\n\tcase \"intersect\":\n\t\tif jsonListsIntersect(searchResult, value) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should intersect with %v\", key, value)\n\tcase \"contains\":\n\t\treturn contains(data, key, value)\n\tcase \"not-contains\":\n\t\treturn doesNotContain(data, key, value)\n\tcase \"does-not-contain\":\n\t\treturn doesNotContain(data, key, value)\n\tcase \"starts-with\":\n\t\treturn startsWith(data, key, value)\n\tcase \"ends-with\":\n\t\treturn endsWith(data, key, value)\n\tcase \"regex\":\n\t\tre, err := regexp.Compile(value)\n\t\tif err != nil {\n\t\t\treturn matchError(err)\n\t\t}\n\t\tif re.MatchString(searchResult) {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v(%v) should match %v\", key, searchResult, value)\n\tcase \"has-properties\":\n\t\treturn hasProperties(data, value)\n\tcase \"is-true\":\n\t\tif searchResult == \"true\" {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be 'true', not '%v'\", key, value)\n\tcase \"is-false\":\n\t\tif searchResult == \"false\" {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be 'false', not '%v'\", key, value)\n\tcase \"is-subnet\":\n\t\tisSubnet := isSubnet(searchResult, value)\n\t\tif isSubnet {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be a subnet of %v\", searchResult, value)\n\tcase \"is-private-ip\":\n\t\tisPrivate := isPrivateIP(searchResult)\n\t\tif isPrivate {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be a private ip\", searchResult)\n\tcase \"max-host-count\":\n\t\thostCountWithinLimit := maxHostCount(searchResult, value)\n\t\tif hostCountWithinLimit {\n\t\t\treturn matches()\n\t\t}\n\t\treturn doesNotMatch(\"%v should be less than or equal to %v\", searchResult, value)\n\t}\n\treturn doesNotMatch(\"unknown op %v\", op)\n}\n"
  },
  {
    "path": "assertion/match_test.go",
    "content": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"testing\"\n)\n\ntype MatchTestCase struct {\n\tSearchResult   interface{}\n\tOp             string\n\tValue          string\n\tValueType      string\n\tExpectedResult bool\n}\n\nfunc getQuotesRight(jsonString string) string {\n\tif len(jsonString) == 0 {\n\t\treturn jsonString\n\t}\n\tif jsonString[0] != '[' {\n\t\tjsonString = quoted(jsonString)\n\t}\n\treturn jsonString\n}\n\nfunc unmarshal(s string) (interface{}, error) {\n\tvar searchResult interface{}\n\tjsonString := getQuotesRight(s)\n\tif len(jsonString) > 0 {\n\t\terr := json.Unmarshal([]byte(jsonString), &searchResult)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn searchResult, nil\n}\n\nfunc TestIsMatch(t *testing.T) {\n\n\tsliceOfTags := []interface{}{\"Foo\", \"Bar\"}\n\temptySlice := []interface{}{}\n\tanotherSlice := []interface{}{\"One\", \"Two\"}\n\tstringSlice := []string{\"One\", \"Two\"}\n\n\ttestCases := map[string]MatchTestCase{\n\t\t\"eqTrue\":                         {\"Foo\", \"eq\", \"Foo\", \"\", true},\n\t\t\"eqFalse\":                        {\"Foo\", \"eq\", \"Bar\", \"\", false},\n\t\t\"eqIntegerTrue\":                  {22, \"eq\", \"22\", \"integer\", true},\n\t\t\"eqIntegerFalse\":                 {80, \"eq\", \"22\", \"integer\", false},\n\t\t\"neFalse\":                        {\"Foo\", \"ne\", \"Foo\", \"\", false},\n\t\t\"neTrue\":                         {\"Foo\", \"ne\", \"Bar\", \"\", true},\n\t\t\"inTrue\":                         {\"Foo\", \"in\", \"Foo,Bar,Baz\", \"\", true},\n\t\t\"inFalse\":                        {\"Foo\", \"in\", \"Bar,Baz\", \"\", false},\n\t\t\"notInFalse\":                     {\"Foo\", \"not-in\", \"Foo,Bar,Baz\", \"\", false},\n\t\t\"notInTrue\":                      {\"Foo\", \"not-in\", \"Bar,Baz\", \"\", true},\n\t\t\"absentFalse\":                    {\"Foo\", \"absent\", \"\", \"\", false},\n\t\t\"absentTrueForEmptyString\":       {\"\", \"absent\", \"\", \"\", true},\n\t\t\"absentTrueForNull\":              {\"null\", \"absent\", \"\", \"\", true},\n\t\t\"absentTrueForEmptyArray\":        {\"[]\", \"absent\", \"\", \"\", true},\n\t\t\"presentTrue\":                    {sliceOfTags, \"present\", \"\", \"\", true},\n\t\t\"presentStringTrue\":              {\"Foo\", \"present\", \"\", \"\", true},\n\t\t\"presentFalseForNil\":             {nil, \"present\", \"\", \"\", false},\n\t\t\"presentFalseForEmptyString\":     {\"\", \"present\", \"\", \"\", false},\n\t\t\"presentFalseForNull\":            {\"null\", \"present\", \"\", \"\", false},\n\t\t\"presentFalseForEmptyArray\":      {\"[]\", \"present\", \"\", \"\", false},\n\t\t\"containsTrueForString\":          {\"Foo\", \"contains\", \"oo\", \"\", true},\n\t\t\"containsFalseForString\":         {\"Foo\", \"contains\", \"aa\", \"\", false},\n\t\t\"containsTrueForSlice\":           {sliceOfTags, \"contains\", \"Bar\", \"\", true},\n\t\t\"containsFalseForSubstring\":      {sliceOfTags, \"contains\", \"abc\", \"\", false},\n\t\t\"containsTrueForSliceOfStrings\":  {stringSlice, \"contains\", \"One\", \"\", true},\n\t\t\"containsFalseForSliceOfStrings\": {stringSlice, \"contains\", \"Three\", \"\", false},\n\t\t\"containsTrueForInt\":             {1, \"contains\", \"1\", \"\", true},\n\t\t\"containsFalseForInt\":            {1, \"contains\", \"One\", \"\", false},\n\t\t\"notContainsFalseForString\":      {\"Foo\", \"does-not-contain\", \"oo\", \"\", false},\n\t\t\"notContainsTrueForString\":       {\"Foo\", \"does-not-contain\", \"aa\", \"\", true},\n\t\t\"notContainsFalseForSlice\":       {sliceOfTags, \"does-not-contain\", \"Bar\", \"\", false},\n\t\t\"notContainsTrueForSubstring\":    {sliceOfTags, \"does-not-contain\", \"abc\", \"\", true},\n\t\t\"regexTrueForEndOfString\":        {\"Foo\", \"regex\", \"o$\", \"\", true},\n\t\t\"regexFalseForEndOfString\":       {\"Bar\", \"regex\", \"o$\", \"\", false},\n\t\t\"regExTrueForBeginningOfString\":  {\"Foo\", \"regex\", \"^F\", \"\", true},\n\t\t\"regExFalseForBeginningOfString\": {\"Foo\", \"regex\", \"^B\", \"\", false},\n\t\t\"reqExFalseForEntireString\":      {\"Foo\", \"regex\", \"^Bar$\", \"\", false},\n\t\t\"regExIgnoreCaseTrue\":            {\"HTTPS\", \"regex\", \"(?i)https\", \"\", true},\n\t\t\"regexIgnoreCaseFalse\":           {\"HTTP\", \"regex\", \"(?i)https\", \"\", false},\n\t\t\"ltTrue\":                         {\"a\", \"lt\", \"b\", \"\", true},\n\t\t\"ltFalse\":                        {\"a\", \"lt\", \"a\", \"\", false},\n\t\t\"leTrue\":                         {\"a\", \"le\", \"a\", \"\", true},\n\t\t\"leFalse\":                        {\"b\", \"le\", \"a\", \"\", false},\n\t\t\"gtTrue\":                         {\"b\", \"gt\", \"a\", \"\", true},\n\t\t\"gtFalse\":                        {\"b\", \"gt\", \"b\", \"\", false},\n\t\t\"geTrue\":                         {\"b\", \"ge\", \"b\", \"\", true},\n\t\t\"geFalse\":                        {\"b\", \"ge\", \"c\", \"\", false},\n\t\t\"nullTrue\":                       {\"\", \"null\", \"\", \"\", true},\n\t\t\"nullFalse\":                      {\"1\", \"null\", \"\", \"\", false},\n\t\t\"notNullFalse\":                   {\"\", \"not-null\", \"\", \"\", false},\n\t\t\"notNullTrue\":                    {\"1\", \"not-null\", \"\", \"\", true},\n\t\t\"emptyTrueForEmptyString\":        {\"\", \"empty\", \"\", \"\", true},\n\t\t\"emptyFalseForString\":            {\"Foo\", \"empty\", \"\", \"\", false},\n\t\t\"emptyTrueForEmptySlice\":         {emptySlice, \"empty\", \"\", \"\", true},\n\t\t\"emptyFalseForSlice\":             {sliceOfTags, \"empty\", \"\", \"\", false},\n\t\t\"notEmptyFalseForEmptyString\":    {\"\", \"not-empty\", \"\", \"\", false},\n\t\t\"notEmptyTrueForString\":          {\"Foo\", \"not-empty\", \"\", \"\", true},\n\t\t\"notEmptyFalseForEmptySlice\":     {emptySlice, \"not-empty\", \"\", \"\", false},\n\t\t\"notEmptyTrueForSlice\":           {sliceOfTags, \"not-empty\", \"\", \"\", true},\n\t\t\"intersectTrue\":                  {\"[\\\"one\\\",\\\"two\\\"]\", \"intersect\", \"[\\\"two\\\",\\\"three\\\"]\", \"\", true},\n\t\t\"intersectFalse\":                 {\"[\\\"one\\\",\\\"two\\\"]\", \"intersect\", \"[\\\"three\\\",\\\"four\\\"]\", \"\", false},\n\t\t\"eqSizeTrue\":                     {anotherSlice, \"eq\", \"2\", \"size\", true},\n\t\t\"eqSizeFalse\":                    {anotherSlice, \"eq\", \"3\", \"size\", false},\n\t\t\"isTrue\":                         {\"true\", \"is-true\", \"\", \"\", true},\n\t\t\"isNotTrue\":                      {\"false\", \"is-true\", \"\", \"\", false},\n\t\t\"isFalse\":                        {\"false\", \"is-false\", \"\", \"\", true},\n\t\t\"isNotFalse\":                     {\"100\", \"is-false\", \"\", \"\", false},\n\t\t\"startsWithTrue\":                 {\"FooBar\", \"starts-with\", \"Foo\", \"\", true},\n\t\t\"startsWithFalse\":                {\"FooBar\", \"starts-with\", \"Bar\", \"\", false},\n\t\t\"startsWithNonString\":            {1, \"starts-with\", \"Foo\", \"\", false},\n\t\t\"endsWithTrue\":                   {\"FooBar\", \"ends-with\", \"Bar\", \"\", true},\n\t\t\"endsWithFalse\":                  {\"FooBar\", \"ends-with\", \"Foo\", \"\", false},\n\t\t\"endsartWithNonString\":           {1, \"ends-with\", \"Foo\", \"\", false},\n\t\t\"isArrayTrue\":                    {sliceOfTags, \"is-array\", \"\", \"\", true},\n\t\t\"isArrayFalse\":                   {\"Foo\", \"is-array\", \"\", \"\", false},\n\t\t\"isNotArrayTrue\":                 {sliceOfTags, \"is-not-array\", \"\", \"\", false},\n\t\t\"isNotArrayFalse\":                {\"Foo\", \"is-not-array\", \"\", \"\", true},\n\t}\n\tfor k, tc := range testCases {\n\t\tvar m MatchResult\n\t\tvar err error\n\t\texpression := Expression{\n\t\t\tKey:       \"key\",\n\t\t\tOp:        tc.Op,\n\t\t\tValue:     tc.Value,\n\t\t\tValueType: tc.ValueType,\n\t\t}\n\t\tif s, isString := tc.SearchResult.(string); isString {\n\t\t\tsearchResult, err := unmarshal(s)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Println(err)\n\t\t\t\tt.Errorf(\"Unable to parse %s\\n\", tc.SearchResult)\n\t\t\t}\n\t\t\tm, err = isMatch(searchResult, expression)\n\t\t} else {\n\t\t\tm, err = isMatch(tc.SearchResult, expression)\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Errorf(\"%s Failed with error: %s\", k, err.Error())\n\t\t}\n\t\tif m.Match != tc.ExpectedResult {\n\t\t\tt.Errorf(\"%s Failed Expected '%s' %s '%s' to be %t\", k, tc.SearchResult, tc.Op, tc.Value, tc.ExpectedResult)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "assertion/rules.go",
    "content": "package assertion\n\nimport (\n\t\"errors\"\n\t\"github.com/ghodss/yaml\"\n)\n\n// ParseRules converts YAML string content to a Result\nfunc ParseRules(rules string) (RuleSet, error) {\n\tr := RuleSet{}\n\terr := yaml.Unmarshal([]byte(rules), &r)\n\treturn r, err\n}\n\n// FilterRulesByTag selects a subset of rules based on a tag\nfunc FilterRulesByTag(rules []Rule, tags []string) []Rule {\n\tfilteredRules := make([]Rule, 0)\n\tfor _, rule := range rules {\n\t\tif tags == nil || listsIntersect(tags, rule.Tags) {\n\t\t\tfilteredRules = append(filteredRules, rule)\n\t\t}\n\t}\n\treturn filteredRules\n}\n\n// FilterRulesByID selectes a subset of rules based on ID\nfunc FilterRulesByID(rules []Rule, ruleIDs []string, ignoreRuleIDs []string) []Rule {\n\tif len(ruleIDs) == 0 && len(ignoreRuleIDs) == 0 {\n\t\treturn rules\n\t}\n\tfilteredRules := make([]Rule, 0)\n\tfor _, rule := range rules {\n\t\tinclude := false\n\t\tfor _, id := range ruleIDs {\n\t\t\tif id == rule.ID {\n\t\t\t\tinclude = true\n\t\t\t}\n\t\t}\n\t\tif len(ignoreRuleIDs) > 0 {\n\t\t\tinclude = true\n\t\t\tfor _, id := range ignoreRuleIDs {\n\t\t\t\tif id == rule.ID {\n\t\t\t\t\tinclude = false\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif include {\n\t\t\tfilteredRules = append(filteredRules, rule)\n\t\t}\n\t}\n\treturn filteredRules\n}\n\nfunc uniqueRules(list []Rule) []Rule {\n\trules := make([]Rule, 0)\n\tkeys := make(map[string]bool, 0)\n\tfor _, rule := range list {\n\t\tif _, ok := keys[rule.ID]; !ok {\n\t\t\tkeys[rule.ID] = true\n\t\t\trules = append(rules, rule)\n\t\t}\n\t}\n\treturn rules\n}\n\n// FilterRulesByTagAndID filters by both tag and id\nfunc FilterRulesByTagAndID(rules []Rule, tags []string, ruleIds []string, ignoreRuleIds []string) []Rule {\n\tif len(tags) == 0 && len(ruleIds) == 0 && len(ignoreRuleIds) == 0 {\n\t\treturn rules\n\t}\n\tif len(tags) == 0 {\n\t\treturn FilterRulesByID(rules, ruleIds, ignoreRuleIds)\n\t}\n\tif len(ruleIds) == 0 {\n\t\treturn FilterRulesByTag(rules, tags)\n\t}\n\treturn uniqueRules(append(FilterRulesByID(rules, ruleIds, ignoreRuleIds), FilterRulesByTag(rules, tags)...))\n}\n\n// ResolveRules loads any dynamic values for a collection or rules\nfunc ResolveRules(rules []Rule, valueSource ValueSource) ([]Rule, []Violation) {\n\tresolvedRules := []Rule{}\n\tviolations := []Violation{}\n\tfor _, rule := range rules {\n\t\tr, vs := ResolveRule(rule, valueSource)\n\t\tresolvedRules = append(resolvedRules, r)\n\t\tviolations = append(violations, vs...)\n\t}\n\treturn resolvedRules, violations\n}\n\n// ResolveRule loads any dynamic values for a single Rule\nfunc ResolveRule(rule Rule, valueSource ValueSource) (Rule, []Violation) {\n\tresolvedRule := rule\n\tresolvedRule.Assertions = []Expression{}\n\tviolations := []Violation{}\n\tfor _, assertion := range rule.Assertions {\n\t\tvalue, err := valueSource.GetValue(assertion)\n\t\tif err != nil {\n\t\t\tDebugf(\"ResolveRule error: %s\\n\", err.Error())\n\t\t\tviolations = append(violations, Violation{\n\t\t\t\tCategory:         \"load\",\n\t\t\t\tRuleID:           \"RULE_RESOLVE\",\n\t\t\t\tResourceID:       rule.ID,\n\t\t\t\tResourceType:     \"rule\",\n\t\t\t\tStatus:           \"FAILURE\",\n\t\t\t\tRuleMessage:      \"Unable to resolve value in rule\",\n\t\t\t\tAssertionMessage: err.Error(),\n\t\t\t\tCreatedAt:        currentTime(),\n\t\t\t})\n\t\t}\n\t\tresolvedAssertion := assertion\n\t\tresolvedAssertion.Value = value\n\t\tresolvedAssertion.ValueFrom = ValueFrom{}\n\t\tresolvedRule.Assertions = append(resolvedRule.Assertions, resolvedAssertion)\n\t}\n\treturn resolvedRule, violations\n}\n\n// CheckRule returns a list of violations for a single Rule applied to a single Resource\nfunc CheckRule(rule Rule, resource Resource, e ExternalRuleInvoker) (string, []Violation, error) {\n\treturnStatus := \"OK\"\n\tviolations := make([]Violation, 0)\n\tif ExcludeResource(rule, resource) {\n\t\tDebugf(\"Ignoring resource: %s\", resource.ID)\n\t\treturn returnStatus, violations, nil\n\t}\n\tif rule.Invoke.URL != \"\" {\n\t\treturn e.Invoke(rule, resource)\n\t}\n\tmatch, err := andExpression(rule.Conditions, resource)\n\tif err != nil {\n\t\treturn \"FAILURE\", violations, err\n\t}\n\tif !match.Match {\n\t\treturn returnStatus, violations, nil\n\t}\n\tfor _, ruleAssertion := range rule.Assertions {\n\t\tDebugf(\"Checking Category: %s, Type: %s, Id: %s\\n\", resource.Category, resource.Type, resource.ID)\n\t\texpressionResult, err := CheckExpression(rule, ruleAssertion, resource)\n\t\tif err != nil {\n\t\t\treturn \"FAILURE\", violations, err\n\t\t}\n\t\tif expressionResult.Status != \"OK\" {\n\t\t\t// If the rule has category (e.g. Terraform rules), then return violations for that category only.\n\t\t\t// If the rule has no category it will be applied to all resources as normal.\n\t\t\tif rule.Category != \"\" && rule.Category != resource.Category {\n\t\t\t    break\n\t\t\t}\n\t\t\treturnStatus = expressionResult.Status\n\t\t\tv := Violation{\n\t\t\t\tRuleID:           rule.ID,\n\t\t\t\tResourceID:       resource.ID,\n\t\t\t\tResourceType:     resource.Type,\n\t\t\t\tCategory:         resource.Category,\n\t\t\t\tStatus:           expressionResult.Status,\n\t\t\t\tRuleMessage:      rule.Message,\n\t\t\t\tAssertionMessage: expressionResult.Message,\n\t\t\t\tFilename:         resource.Filename,\n\t\t\t\tLineNumber:       resource.LineNumber,\n\t\t\t\tCreatedAt:        currentTime(),\n\t\t\t}\n\t\t\tviolations = append(violations, v)\n\t\t}\n\t}\n\treturn returnStatus, violations, nil\n}\n\n// Join two RuleSets together\nfunc JoinRuleSets(firstSet RuleSet, secondSet RuleSet) (RuleSet, error) {\n\t// if one of the sets is empty, return the other\n\t// if both are empty, an empty set is returned\n\tif len(firstSet.Rules) == 0 {\n\t\treturn secondSet, nil\n\t} else if len(secondSet.Rules) == 0 {\n\t\treturn firstSet, nil\n\t}\n\n\t// RuleSets must match Type and Version\n\t// Description will be taken from the first given rule set\n\tif firstSet.Type != secondSet.Type || firstSet.Version != secondSet.Version {\n\t\treturn firstSet, errors.New(\"RuleSet Type and Version must match\")\n\t} else {\n\t\tjoinedSet := RuleSet{}\n\t\tjoinedSet.Type = firstSet.Type\n\t\tjoinedSet.Description = firstSet.Description\n\t\tjoinedSet.Files = append(firstSet.Files, secondSet.Files...)\n\t\tjoinedSet.Rules = append(firstSet.Rules, secondSet.Rules...)\n\t\tjoinedSet.Version = firstSet.Version\n\t\tjoinedSet.Resources = append(firstSet.Resources, secondSet.Resources...)\n\t\tjoinedSet.Columns = append(firstSet.Columns, secondSet.Columns...)\n\t\treturn joinedSet, nil\n\t}\n}\n"
  },
  {
    "path": "assertion/rules_test.go",
    "content": "package assertion\n\nimport (\n\t\"errors\"\n\t\"testing\"\n)\n\n// TestValueSource provides test values\n\ntype TestValueSource struct{}\n\nfunc (t TestValueSource) GetValue(expression Expression) (string, error) {\n\tif expression.Value != \"\" {\n\t\treturn expression.Value, nil\n\t}\n\treturn \"m3.medium\", nil\n}\n\nfunc testValueSource() ValueSource {\n\treturn TestValueSource{}\n}\n\n// TestValueSourceWithError simulates errors for value provider\n\ntype TestValueSourceWithError struct{}\n\nfunc (t TestValueSourceWithError) GetValue(expression Expression) (string, error) {\n\treturn \"\", errors.New(\"GET_VALUE_ERROR\")\n}\n\nfunc testValueSourceWithError() ValueSource {\n\treturn TestValueSourceWithError{}\n}\n\n// MockExternalRuleInvoker simulates invocation of external endpoints to get values\n\ntype MockExternalRuleInvoker int\n\nfunc mockExternalRuleInvoker() *MockExternalRuleInvoker {\n\tvar m MockExternalRuleInvoker\n\treturn &m\n}\n\nfunc (e *MockExternalRuleInvoker) Invoke(Rule, Resource) (string, []Violation, error) {\n\t*e++\n\tnoViolations := make([]Violation, 0)\n\treturn \"OK\", noViolations, nil\n}\n\nvar content = `Rules:\n  - id: TEST1\n    message: Test message\n    resource: aws_instance\n    severity: WARNING\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro\n    tags:\n      - ec2\n  - id: TEST2\n    message: Test message\n    resource: aws_s3_bucket\n    severity: WARNING\n    assertions:\n      - key: name\n        op: eq\n        value: bucket1\n    tags:\n      - s3\n  - id: TEST3\n    message: Test message\n    resource: aws_ebs_volume\n    severity: WARNING\n    assertions:\n      - key: size\n        op: le\n        value: 1000\n        value_type: integer\n    tags:\n      - ebs\n`\n\nfunc MustParseRules(content string, t *testing.T) RuleSet {\n\tr, err := ParseRules(content)\n\tif err != nil {\n\t\tt.Error(\"Unable to parse:\" + content)\n\t}\n\treturn r\n}\n\nfunc TestParseRules(t *testing.T) {\n\tr := MustParseRules(content, t)\n\tif len(r.Rules) != 3 {\n\t\tt.Error(\"Expected to parse 3 rules\")\n\t}\n}\n\ntype FilterTestCase struct {\n\tTags          []string\n\tIds           []string\n\tIgnoreIds     []string\n\tExpectedRules []string\n}\n\nfunc TestFilterRules(t *testing.T) {\n\n\tvar emptyTags []string\n\tvar emptyIds []string\n\n\ttestCases := map[string]FilterTestCase{\n\t\t\"allRules\": FilterTestCase{emptyTags, emptyIds, emptyIds, []string{\"TEST1\", \"TEST2\", \"TEST3\"}},\n\t\t\"tags\":     FilterTestCase{[]string{\"s3\"}, emptyIds, emptyIds, []string{\"TEST2\"}},\n\t\t\"rules\":    FilterTestCase{emptyTags, []string{\"TEST1\"}, emptyIds, []string{\"TEST1\"}},\n\t\t\"both\":     FilterTestCase{[]string{\"s3\"}, []string{\"TEST1\"}, emptyIds, []string{\"TEST1\", \"TEST2\"}},\n\t\t\"overlap\":  FilterTestCase{[]string{\"s3\"}, []string{\"TEST2\"}, emptyIds, []string{\"TEST2\"}},\n\t\t\"exclude\":  FilterTestCase{emptyTags, emptyIds, []string{\"TEST1\"}, []string{\"TEST2\", \"TEST3\"}},\n\t}\n\tfor k, tc := range testCases {\n\t\tr := FilterRulesByTagAndID(MustParseRules(content, t).Rules, tc.Tags, tc.Ids, tc.IgnoreIds)\n\t\tif len(r) != len(tc.ExpectedRules) {\n\t\t\tt.Errorf(\"Expected %s to include %d rules not %d\\n\", k, len(tc.ExpectedRules), len(r))\n\t\t}\n\t}\n}\n\nfunc TestFilterRulesByTagAndID(t *testing.T) {\n\ttags := []string{\"s3\"}\n\tids := []string{\"TEST3\"}\n\tr := FilterRulesByTagAndID(MustParseRules(content, t).Rules, tags, ids, []string{})\n\tif len(r) != 2 {\n\t\tt.Error(\"Expected filterRulesByTag to return 2 rules\")\n\t}\n\tfor _, rule := range r {\n\t\tif rule.ID != \"TEST2\" && rule.ID != \"TEST3\" {\n\t\t\tt.Error(\"Expected filterRulesByTagAndID to select correct rules\")\n\t\t}\n\t}\n}\n\nvar ruleWithMultipleFilters = `Rules:\n  - id: TEST1\n    message: Test message\n    resource: aws_instance\n    severity: FAILURE\n    assertions:\n      - key: instance_type\n        op: eq\n        value: t2.micro\n      - key: ami\n        op: eq\n        value: ami-000000\n`\n\nfunc TestRuleWithMultipleFilter(t *testing.T) {\n\trules := MustParseRules(ruleWithMultipleFilters, t)\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{\"instance_type\": \"t2.micro\", \"ami\": \"ami-000000\"},\n\t\tFilename:   \"test.tf\",\n\t}\n\tstatus, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())\n\tif err != nil {\n\t\tt.Error(\"Error in CheckRule:\" + err.Error())\n\t}\n\tif status != \"OK\" {\n\t\tt.Error(\"Expecting multiple rule to match\")\n\t}\n\tif len(violations) != 0 {\n\t\tt.Error(\"Expecting multiple rule to have zero violations\")\n\t}\n}\n\nfunc TestMultipleFiltersWithSingleFailure(t *testing.T) {\n\trules := MustParseRules(ruleWithMultipleFilters, t)\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{\"instance_type\": \"t2.micro\", \"ami\": \"ami-111111\"},\n\t\tFilename:   \"test.tf\",\n\t}\n\tstatus, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())\n\tif err != nil {\n\t\tt.Error(\"Error in CheckRule:\" + err.Error())\n\t}\n\tif status != \"FAILURE\" {\n\t\tt.Error(\"Expecting multiple rule to return FAILURE\")\n\t}\n\tif len(violations) != 1 {\n\t\tt.Error(\"Expecting multiple rule to have one violation\")\n\t}\n}\n\nfunc TestMultipleFiltersWithMultipleFailures(t *testing.T) {\n\trules := MustParseRules(ruleWithMultipleFilters, t)\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{\"instance_type\": \"c3.medium\", \"ami\": \"ami-111111\"},\n\t\tFilename:   \"test.tf\",\n\t}\n\tstatus, violations, err := CheckRule(rules.Rules[0], resource, mockExternalRuleInvoker())\n\tif err != nil {\n\t\tt.Error(\"Error in CheckRule:\" + err.Error())\n\t}\n\tif status != \"FAILURE\" {\n\t\tt.Error(\"Expecting multiple rule to return FAILURE\")\n\t}\n\tif len(violations) != 2 {\n\t\tt.Error(\"Expecting multiple rule to have two violations\")\n\t}\n}\n\nvar ruleWithValueFrom = `Rules:\n  - id: FROM1\n    message: Test value_from\n    severity: FAILURE\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          bucket: config-rules-for-lambda\n          key: instance-types\n`\n\nfunc TestValueFrom(t *testing.T) {\n\trules := MustParseRules(ruleWithValueFrom, t)\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{\"instance_type\": \"m3.medium\"},\n\t\tFilename:   \"test.tf\",\n\t}\n\tresolved, violations := ResolveRules(rules.Rules, testValueSource())\n\tif len(violations) != 0 {\n\t\tt.Errorf(\"Expecting ResolveRules to return 0 violations: %v\", violations)\n\t}\n\tstatus, violations, err := CheckRule(resolved[0], resource, mockExternalRuleInvoker())\n\tif err != nil {\n\t\tt.Error(\"Error in CheckRule:\" + err.Error())\n\t}\n\tif status != \"OK\" {\n\t\tt.Error(\"Expecting value_from to match\")\n\t}\n\tif len(violations) != 0 {\n\t\tt.Error(\"Expecting value_from test to have 0 violations\")\n\t}\n}\n\nfunc TestResolveRuleError(t *testing.T) {\n\trules := MustParseRules(ruleWithValueFrom, t)\n\t_, violations := ResolveRules(rules.Rules, testValueSourceWithError())\n\tif len(violations) != 1 {\n\t\tt.Errorf(\"Expecting ResolveRules to return 1 violations: %v\", violations)\n\t} else {\n\t\truleID := violations[0].RuleID\n\t\tif ruleID != \"RULE_RESOLVE\" {\n\t\t\tt.Errorf(\"Expected RULE_RESOLVE violation, not %s\", ruleID)\n\t\t}\n\t}\n}\n\nvar ruleWithInvoke = `Rules:\n  - id: FROM1\n    message: Test value_from\n    severity: FAILURE\n    resource: aws_instance\n    invoke:\n      url: http://localhost\n`\n\nfunc TestInvokeRule(t *testing.T) {\n\trules := MustParseRules(ruleWithInvoke, t)\n\tresource := Resource{\n\t\tID:         \"a_test_resource\",\n\t\tType:       \"aws_instance\",\n\t\tProperties: map[string]interface{}{\"instance_type\": \"m3.medium\"},\n\t\tFilename:   \"test.tf\",\n\t}\n\tresolved, _ := ResolveRules(rules.Rules, testValueSource())\n\tcounter := mockExternalRuleInvoker()\n\tCheckRule(resolved[0], resource, counter)\n\tif *counter != 1 {\n\t\tt.Error(\"Expecting external rule engine to be invoked\")\n\t}\n}\n"
  },
  {
    "path": "assertion/search.go",
    "content": "package assertion\n\nimport (\n\t\"github.com/jmespath/go-jmespath\"\n)\n\n// SearchData applies a JMESPath to a JSON object\nfunc SearchData(expression string, data interface{}) (interface{}, error) {\n\tif len(expression) == 0 {\n\t\treturn \"null\", nil\n\t}\n\n\treturn jmespath.Search(expression, data)\n}\n"
  },
  {
    "path": "assertion/testdata/collection-assertions.yaml",
    "content": "---\ndescription: Test collection assertions\ntest_cases:\n\n  - name: every_OK\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - every:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Foo:\n          - A\n          - B\n          - C\n        Bar:\n          - D\n          - E\n    result: OK\n\n  - name: every_FAILURE\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - every:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Foo:\n          - A\n          - B\n          - C\n        Bar:\n          - D\n          - E\n        Baz:\n          - F\n    result: FAILURE\n\n  - name: every_multiple_assertions_FAILURE\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - every:\n            key: locations\n            expressions:\n              - key: city\n                op: present\n              - key: state\n                op: present\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        locations:\n          - city: Seattle\n            state: WA\n          - city: San Francisco\n    result: FAILURE\n\n  - name: some_OK\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - some:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Foo:\n          - A\n          - B\n          - C\n        Baz:\n          - D\n          - E\n    result: OK\n\n  - name: some_FAILURE\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - some:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Baz:\n          - A\n    result: FAILURE\n\n  - name: none_OK\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - none:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Baz:\n          - A\n          - B\n    result: OK\n\n  - name: none_with_multiple_assertions_OK\n    rule:\n      id: COLLECTION\n      message: Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - none:\n            key: \"ipPermissions[]\"\n            expressions:\n              - key: \"fromPort\"\n                op: eq\n                value: 22\n                value_type: integer\n              - key: \"ipRanges[]\"\n                op: contains\n                value: 0.0.0.0/0\n    resource:\n      id: collection_id\n      type: sample\n      properties:\n        ipPermissions:\n          - fromPort: 80\n            ipRanges:\n              - 0.0.0.0/0\n    result: OK\n\n  - name: none_FAILURE\n    rule:\n      id:       COLLECTION\n      message:  Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - none:\n            key: \"keys(@)\"\n            expressions:\n              - key: \"@\"\n                op: in\n                value: Foo,Bar\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        Foo:\n          - A\n        Bar:\n          - B\n    result: FAILURE\n\n  - name: none_with_multiple_assertions_FAILURE\n    rule:\n      id: COLLECTION\n      message: Invalid key\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - none:\n            key: \"ipPermissions[]\"\n            expressions:\n              - key: \"fromPort\"\n                op: eq\n                value: 22\n                value_type: integer\n              - key: \"ipRanges[]\"\n                op: contains\n                value: 0.0.0.0/0\n    resource:\n      id: collection_id\n      type: sample\n      properties:\n        ipPermissions:\n          - fromPort: 22\n            ipRanges:\n              - 0.0.0.0/0\n    result: FAILURE\n\n  - name: one_OK\n    rule:\n      id:       COLLECTION\n      message:  Duplicate names\n      severity: FAILURE\n      resource: example\n      assertions:\n        - exactly-one:\n            key: \"tags[]\"\n            expressions:\n              - key: name\n                op: eq\n                value: A\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        tags:\n          - name: A\n          - name: B\n    result: OK\n\n  - name: one_FAILURE\n    rule:\n      id:       COLLECTION\n      message:  Duplicate names\n      severity: FAILURE\n      resource: example\n      assertions:\n        - exactly-one:\n            key: \"tags[]\"\n            expressions:\n              - key: name\n                op: eq\n                value: B\n    resource:\n      id: collection_id\n      type: example\n      properties:\n        tags:\n          - name: A\n          - name: B\n          - name: B\n    result: FAILURE\n\n"
  },
  {
    "path": "assertion/testdata/conditions.yaml",
    "content": "---\ndescription: Test conditions\ntest_cases:\n\n  - name: conditions_false\n    rule:\n      id: CONDITIONS_1\n      message: Missing properties\n      severity: FAILURE\n      resource: sample\n      conditions:\n        - key: example.name\n          eq: first\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n          id: 1\n    result: OK\n\n  - name: conditions_ignore\n    rule:\n      id: PROPERTIES_2\n      message: Ignore using condition\n      severity: FAILURE\n      resource: sample\n      conditions:\n        - key: example.name\n          op: eq\n          value: second\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id,description\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n          id: 1\n    result: OK\n\n  - name: conditions_FAILURE\n    rule:\n      id: PROPERTIES_2\n      message: Missing properties\n      severity: FAILURE\n      resource: sample\n      conditions:\n        - key: example.name\n          op: eq\n          value: first\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id,description\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n          id: 1\n    result: FAILURE\n"
  },
  {
    "path": "assertion/testdata/default-severity.yaml",
    "content": "---\ndescription: Test uses default severity\ntest_cases:\n\n  - name: default-severity-FAILURE\n    rule:\n      id: PROPERTIES_1\n      message: Missing properties\n      resource: sample\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n    result: FAILURE\n"
  },
  {
    "path": "assertion/testdata/has-properties.yaml",
    "content": "---\ndescription: Test has-properties operator\ntest_cases:\n\n  - name: has-properties_OK\n    rule:\n      id: PROPERTIES_1\n      message: Missing properties\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n          id: 1\n    result: OK\n\n  - name: has-properties_FAILURE\n    rule:\n      id: PROPERTIES_2\n      message: Missing properties\n      severity: FAILURE\n      resource: sample\n      assertions:\n        - key: example\n          op: has-properties\n          value: name,id,description\n    resource:\n      id: p1\n      type: sample\n      properties:\n        example:\n          name: first\n          id: 1\n    result: FAILURE\n"
  },
  {
    "path": "assertion/types.go",
    "content": "package assertion\n\ntype (\n\n\t// Resource describes a resource to be linted\n\tResource struct {\n\t\tID         string `cty:\"aws_instance\"`\n\t\tType       string\n\t\tCategory   string // default is \"resource\", can be \"data\", \"provider\" for Terraform\n\t\tProperties interface{}\n\t\tFilename   string\n\t\tLineNumber int\n\t}\n\n\t// RuleSet describes a collection of rules for a Linter\n\tRuleSet struct {\n\t\tType        string\n\t\tDescription string\n\t\tFiles       []string\n\t\tRules       []Rule\n\t\tVersion     string\n\t\tResources   []ResourceConfig\n\t\tColumns     []ColumnConfig\n\t\tSource      string\n\t}\n\n\t// Rule is part of a RuleSet\n\tRule struct {\n\t\tID              string\n\t\tMessage         string\n\t\tSeverity        string\n\t\tResource        string\n\t\tResources       []string\n\t\tExceptResources []string `json:\"except_resources\"`\n\t\tCategory        string   // default is \"resource\", can be \"data\", \"provider\", \"module\" for Terraform\n\t\tConditions      []Expression\n\t\tAssertions      []Expression\n\t\tExcept          []string\n\t\tTags            []string\n\t\tInvoke          InvokeRuleAPI\n\t}\n\n\t// Expression expression for a Rule\n\tExpression struct {\n\t\tKey        string\n\t\tOp         string\n\t\tValue      string\n\t\tValueType  string    `json:\"value_type\"`\n\t\tValueFrom  ValueFrom `json:\"value_from\"`\n\t\tOr         []Expression\n\t\tXor        []Expression\n\t\tAnd        []Expression\n\t\tNot        []Expression\n\t\tEvery      CollectionExpression\n\t\tSome       CollectionExpression\n\t\tNone       CollectionExpression\n\t\tExactlyOne CollectionExpression `json:\"exactly-one\"`\n\t}\n\n\t// CollectionExpression assertion for every element of a collection\n\tCollectionExpression struct {\n\t\tKey         string\n\t\tExpressions []Expression\n\t}\n\n\t// ValueFrom describes a external source for values\n\tValueFrom struct {\n\t\tURL      string\n\t\tVariable string\n\t}\n\n\t// InvokeRuleAPI describes an external API for linting a resource\n\tInvokeRuleAPI struct {\n\t\tURL     string\n\t\tPayload string\n\t}\n\n\t// ResourceConfig describes how to discover resouces in a YAML file\n\tResourceConfig struct {\n\t\tID   string\n\t\tType string\n\t\tKey  string\n\t}\n\n\t// ColumnConfig describes how to discover resources in a CSV file\n\tColumnConfig struct {\n\t\tName string\n\t}\n\n\t// ValidationReport summarizes validation for resources using rules\n\tValidationReport struct {\n\t\tFilesScanned     []string\n\t\tViolations       []Violation\n\t\tResourcesScanned []ScannedResource\n\t}\n\n\t// Violation has details for a failed assertion\n\tViolation struct {\n\t\tRuleID           string\n\t\tResourceID       string\n\t\tResourceType     string\n\t\tCategory         string\n\t\tStatus           string\n\t\tRuleMessage      string\n\t\tAssertionMessage string\n\t\tFilename         string\n\t\tLineNumber       int\n\t\tCreatedAt        string\n\t}\n\n\t// ScannedResource has details for each resource scanned\n\tScannedResource struct {\n\t\tResourceID   string\n\t\tResourceType string\n\t\tRuleID       string\n\t\tStatus       string\n\t\tFilename     string\n\t\tLineNumber   int\n\t}\n\n\t// ValueSource interface to fetch dynamic values\n\tValueSource interface {\n\t\tGetValue(Expression) (string, error)\n\t}\n\n\t// ExternalRuleInvoker defines an interface for invoking an external API\n\tExternalRuleInvoker interface {\n\t\tInvoke(Rule, Resource) (string, []Violation, error)\n\t}\n\n\t// MatchResult has a true/false result, but also includes a message for better reporting\n\tMatchResult struct {\n\t\tMatch   bool\n\t\tMessage string\n\t}\n\n\t// Result returns a status, along with a message\n\tResult struct {\n\t\tStatus  string\n\t\tMessage string\n\t}\n)\n"
  },
  {
    "path": "assertion/util.go",
    "content": "package assertion\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"time\"\n)\n\nfunc unquoted(s string) string {\n\tif s[0] == '\"' {\n\t\treturn s[1 : len(s)-1]\n\t}\n\treturn s\n}\n\nfunc quoted(s string) string {\n\treturn fmt.Sprintf(\"\\\"%s\\\"\", s)\n}\n\nfunc isAbsent(s string) bool {\n\tif s == \"\" || s == \"null\" || s == \"[]\" {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc isPresent(s string) bool {\n\treturn !isAbsent(s)\n}\n\nfunc isEmpty(data interface{}) bool {\n\tswitch v := data.(type) {\n\tcase nil:\n\t\treturn true\n\tcase string:\n\t\treturn len(v) == 0\n\tcase []interface{}:\n\t\treturn len(v) == 0\n\tcase []map[string]interface{}:\n\t\treturn len(v) == 0\n\tdefault:\n\t\tDebugf(\"isEmpty default: %v %T\\n\", data, data)\n\t\treturn false\n\t}\n}\n\nfunc isArray(data interface{}) bool {\n\tswitch data.(type) {\n\tcase nil:\n\t\treturn false\n\tcase string:\n\t\treturn false\n\tcase []interface{}:\n\t\treturn true\n\tcase []map[string]interface{}:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc listsIntersect(list1 []string, list2 []string) bool {\n\tfor _, a := range list1 {\n\t\tfor _, b := range list2 {\n\t\t\tif a == b {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc jsonListsIntersect(s1 string, s2 string) bool {\n\tvar a1 []string\n\tvar a2 []string\n\terr := json.Unmarshal([]byte(s1), &a1)\n\tif err != nil {\n\t\treturn false\n\t}\n\terr = json.Unmarshal([]byte(s2), &a2)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn listsIntersect(a1, a2)\n}\n\n// ShouldIncludeFile return true if a filename matches one of a list of patterns\nfunc ShouldIncludeFile(patterns []string, filename string) (bool, error) {\n\tif filename == \"-\" { // always permit stdin\n\t\treturn true, nil\n\t}\n\tfor _, pattern := range patterns {\n\t\t_, file := filepath.Split(filename)\n\t\tmatched, err := filepath.Match(pattern, file)\n\t\tif err != nil {\n\t\t\treturn false, err\n\t\t}\n\t\tif matched {\n\t\t\treturn true, nil\n\t\t}\n\t}\n\treturn false, nil\n}\n\n// FilterResourcesByType filters a list of resources that match a single resource type\nfunc FilterResourcesByType(resources []Resource, resourceType string, resourceCategory string) []Resource {\n\tif resourceType == \"*\" {\n\t\treturn resources\n\t}\n\tfiltered := make([]Resource, 0)\n\tfor _, resource := range resources {\n\t\tif resource.Type == resourceType && categoryMatches(resourceCategory, resource.Category) {\n\t\t\tfiltered = append(filtered, resource)\n\t\t}\n\t}\n\treturn filtered\n}\n\n// FilterResourcesByTypes filters a list of resources that match a slice of resource types\nfunc FilterResourcesByTypes(resources []Resource, resourceTypes []string, resourceCategory string) []Resource {\n\tfiltered := make([]Resource, 0)\n\tfor _, resource := range resources {\n\t\tif SliceContains(resourceTypes, resource.Type) && categoryMatches(resourceCategory, resource.Category) {\n\t\t\tfiltered = append(filtered, resource)\n\t\t}\n\t}\n\treturn filtered\n}\n\nfunc categoryMatches(c1, c2 string) bool {\n\tif c1 == \"\" || c1 == \"*\" {\n\t\treturn true\n\t}\n\treturn c1 == c2\n}\n\n// JSONStringify converts a JSON object into an indented string suitable for printing\nfunc JSONStringify(data interface{}) (string, error) {\n\tb, err := json.MarshalIndent(data, \"\", \"  \")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn string(b), nil\n}\n\nfunc currentTime() string {\n\treturn time.Now().UTC().Format(time.RFC3339)\n}\n\nfunc SliceContains(list []string, value string) bool {\n\tfor _, item := range list {\n\t\tif item == value {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Exclude resources\nfunc ExcludeResourceTypes(resources []Resource, resourceTypes []string, resourceCategory string) []Resource {\n\tfiltered := make([]Resource, 0)\n\tfor _, resource := range resources {\n\t\tif !SliceContains(resourceTypes, resource.Type) && categoryMatches(resourceCategory, resource.Category) {\n\t\t\tfiltered = append(filtered, resource)\n\t\t}\n\t}\n\treturn filtered\n}\n\n// FilterResourcesForRule returns resources applicable to the given rule\nfunc FilterResourcesForRule(resources []Resource, rule Rule) []Resource {\n\tif len(rule.Resources) > 0 {\n\t\tDebugf(\"filtering rule resources on Resources slice\")\n\t\treturn FilterResourcesByTypes(resources, rule.Resources, rule.Category)\n\t}\n\tif rule.Resource != \"\" && rule.Resource != \"*\" {\n\t\tDebugf(\"filtering rule resources on Resource string\")\n\t\treturn FilterResourcesByType(resources, rule.Resource, rule.Category)\n\t}\n\tif len(rule.ExceptResources) > 0 {\n\t\tDebugf(\"filtering rule resources on ExceptResources slice\")\n\t\treturn ExcludeResourceTypes(resources, rule.ExceptResources, rule.Category)\n\t}\n\t// default is to match all resources\n\treturn resources\n}\n"
  },
  {
    "path": "assertion/util_test.go",
    "content": "package assertion\n\nimport (\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestUnquotedWithoutQuotes(t *testing.T) {\n\tif unquoted(\"Foo\") != \"Foo\" {\n\t\tt.Errorf(\"Unquoted for not quoted string fails\")\n\t}\n}\n\nfunc TestUnquotedWithQuotes(t *testing.T) {\n\tif unquoted(\"\\\"Foo\\\"\") != \"Foo\" {\n\t\tt.Errorf(\"Unquoted for quoted string fails\")\n\t}\n}\n\nfunc TestIsAbsentEmptyString(t *testing.T) {\n\tif isAbsent(\"\") != true {\n\t\tt.Errorf(\"isAbsent for empty string fails\")\n\t}\n}\n\nfunc TestIsAbsentEmptyArray(t *testing.T) {\n\tif isAbsent(\"[]\") != true {\n\t\tt.Errorf(\"isAbsent for empty array fails\")\n\t}\n}\n\nfunc TestIsAbsentNull(t *testing.T) {\n\tif isAbsent(\"null\") != true {\n\t\tt.Errorf(\"isAbsent for null fails\")\n\t}\n}\n\nfunc TestIsAbsentFalse(t *testing.T) {\n\tif isAbsent(\"something\") != false {\n\t\tt.Errorf(\"isAbsent for value fails\")\n\t}\n}\n\nfunc TestIntersectTrue(t *testing.T) {\n\ta := []string{\"foo\", \"bar\"}\n\tb := []string{\"bar\", \"baz\"}\n\tif listsIntersect(a, b) != true {\n\t\tt.Errorf(\"listsIntersect should return true fails\")\n\t}\n}\n\nfunc TestIntersectFalse(t *testing.T) {\n\ta := []string{\"foo\", \"bar\"}\n\tb := []string{\"baz\"}\n\tif listsIntersect(a, b) != false {\n\t\tt.Errorf(\"listsIntersect should return false fails\")\n\t}\n}\n\nfunc TestJSONListsIntersectTrue(t *testing.T) {\n\ts1 := \"[ \\\"foo\\\", \\\"bar\\\" ]\"\n\ts2 := \"[ \\\"baz\\\", \\\"bar\\\" ]\"\n\tif jsonListsIntersect(s1, s2) != true {\n\t\tt.Errorf(\"JSONIntersect should return true\")\n\t}\n}\n\nfunc TestShouldIncludeFile(t *testing.T) {\n\tpatterns := []string{\"*.tf\", \"*.yml\"}\n\tinclude, err := ShouldIncludeFile(patterns, \"instance.tf\")\n\tif err != nil {\n\t\tt.Errorf(\"ShouldIncludeFile generated an unexpected error: %v\", err)\n\t}\n\tif !include {\n\t\tt.Errorf(\"ShouldIncludeFile failed to include file with matching pattern\")\n\t}\n}\n\nfunc TestShouldNotIncludeFile(t *testing.T) {\n\tpatterns := []string{\"*.tf\", \"*.yml\"}\n\tinclude, err := ShouldIncludeFile(patterns, \"instance.config\")\n\tif err != nil {\n\t\tt.Errorf(\"ShouldIncludeFile generated an unexpected error: %v\", err)\n\t}\n\tif include {\n\t\tt.Errorf(\"ShouldIncludeFile failed to exclude file with no matching pattern\")\n\t}\n}\n\nfunc TestFilterShouldIncludeResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\"},\n\t\tResource{Type: \"volume\"},\n\t}\n\tfiltered := FilterResourcesByType(resources, \"instance\", \"*\")\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"FilterResourcesByType expected to match one resource\")\n\t}\n}\n\nfunc TestFilterShouldExcludeResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\"},\n\t\tResource{Type: \"volume\"},\n\t}\n\tfiltered := FilterResourcesByType(resources, \"database\", \"*\")\n\tif len(filtered) != 0 {\n\t\tt.Errorf(\"FilterResourcesByType expected to match no resources\")\n\t}\n}\n\nfunc TestFilterShouldIncludeAllResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\"},\n\t\tResource{Type: \"volume\"},\n\t}\n\tfiltered := FilterResourcesByType(resources, \"*\", \"*\")\n\tif len(filtered) != len(resources) {\n\t\tt.Errorf(\"FilterResourcesByType expected to include all resources\")\n\t}\n}\n\nfunc TestFilterShouldMatchCategoryForResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"template_file\", Category: \"data\"},\n\t}\n\tfiltered := FilterResourcesByType(resources, \"template_file\", \"data\")\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"FilterResourcesByType expected to match one resource\")\n\t}\n}\n\nfunc TestSliceContainsTrue(t *testing.T) {\n\ttest := []string{\"x\", \"y\", \"z\"}\n\tisPresent := SliceContains(test, \"x\")\n\tif isPresent != true {\n\t\tt.Errorf(\"SliceContains expected to return true when a value is present\")\n\t}\n}\n\nfunc TestSliceContainsFalse(t *testing.T) {\n\ttest := []string{\"x\", \"y\", \"z\"}\n\tisPresent := SliceContains(test, \"a\")\n\tif isPresent != false {\n\t\tt.Errorf(\"SliceContains expected to return false when a value is not present\")\n\t}\n}\n\nfunc TestFilterPluralShouldMatchMultipleResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\tfiltered := FilterResourcesByTypes(resources, []string{\"instance\", \"bucket\"}, \"resource\")\n\tif len(filtered) != 2 {\n\t\tt.Errorf(\"FilterResourcesByTypes expected to match multiple types\")\n\t}\n}\n\nfunc TestFilterPluralShouldNotHaveUnlistedResources(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\tresourceTypes := []string{\"instance\"}\n\tfiltered := FilterResourcesByTypes(resources, resourceTypes, \"resource\")\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"FilterResourcesByTypes expected to match only %s\", strings.Join(resourceTypes, \", \"))\n\t}\n}\n\nfunc TestFilterResourcesForRuleSlice(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\trule := Rule{\n\t\tResources: []string{\n\t\t\t\"instance\",\n\t\t\t\"bucket\",\n\t\t},\n\t}\n\tfiltered := FilterResourcesForRule(resources, rule)\n\tif len(filtered) != 2 {\n\t\tt.Errorf(\"FilterResourcesForRule expected to return both resource types\")\n\t}\n}\n\nfunc TestFilterResourcesForRuleString(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\trule := Rule{\n\t\tResource: \"instance\",\n\t}\n\tfiltered := FilterResourcesForRule(resources, rule)\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"FilterResourcesForRule only expected to return one type\")\n\t}\n}\n\nfunc TestFilterResourcesForWildcard(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\trule := Rule{\n\t\tResource: \"*\",\n\t}\n\tfiltered := FilterResourcesForRule(resources, rule)\n\tif len(filtered) != 2 {\n\t\tt.Errorf(\"FilterResourcesForRule expected all resources to match\")\n\t}\n}\n\nfunc TestFilterResourcesForDefault(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\trule := Rule{}\n\tfiltered := FilterResourcesForRule(resources, rule)\n\tif len(filtered) != 2 {\n\t\tt.Errorf(\"FilterResourcesForRule expected all resources to match\")\n\t}\n}\n\nfunc TestFilterExcludeResourcesForRuleString(t *testing.T) {\n\tresources := []Resource{\n\t\tResource{Type: \"instance\", Category: \"resource\"},\n\t\tResource{Type: \"bucket\", Category: \"resource\"},\n\t}\n\trule := Rule{\n\t\tExceptResources: []string{\n\t\t\t\"instance\",\n\t\t\t\"security_group\",\n\t\t},\n\t}\n\tfiltered := FilterResourcesForRule(resources, rule)\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"FilterResourcesForRule expected to return one type\")\n\t}\n\tif len(filtered) > 0 && filtered[0].Type != \"bucket\" {\n\t\tt.Errorf(\"FilterResourcesForRule expected to return bucket\")\n\t}\n}\n"
  },
  {
    "path": "assertion/value.go",
    "content": "package assertion\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/aws/aws-sdk-go/aws\"\n\t\"github.com/aws/aws-sdk-go/aws/session\"\n\t\"github.com/aws/aws-sdk-go/service/s3\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// StandardValueSource can fetch values from external sources\ntype StandardValueSource struct {\n\tVariables map[string]string\n}\n\n// GetValue looks up external values when an Expression includes a ValueFrom attribute\nfunc (v StandardValueSource) GetValue(expression Expression) (string, error) {\n\tif expression.ValueFrom.URL != \"\" {\n\t\tDebugf(\"Getting value_from %s\\n\", expression.ValueFrom.URL)\n\t\tparsedURL, err := url.Parse(expression.ValueFrom.URL)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tswitch strings.ToLower(parsedURL.Scheme) {\n\t\tcase \"s3\":\n\t\t\treturn v.GetValueFromS3(parsedURL.Host, parsedURL.Path)\n\t\tcase \"http\":\n\t\t\treturn v.GetValueFromHTTP(expression.ValueFrom.URL)\n\t\tcase \"https\":\n\t\t\treturn v.GetValueFromHTTP(expression.ValueFrom.URL)\n\t\tdefault:\n\t\t\treturn \"\", fmt.Errorf(\"Unsupported protocol for value_from: %s\", parsedURL.Scheme)\n\t\t}\n\t}\n\tif expression.ValueFrom.Variable != \"\" {\n\t\tif value, ok := v.Variables[expression.ValueFrom.Variable]; ok {\n\t\t\tDebugf(\"Getting value_from variable %s: %s\\n\", expression.ValueFrom.Variable, value)\n\t\t\treturn value, nil\n\t\t}\n\t\tDebugf(\"Getting value_from variable %s not found\\n\", expression.ValueFrom.Variable)\n\t\treturn expression.ValueFrom.Variable, nil // or should this throw an error?\n\t}\n\treturn expression.Value, nil\n}\n\n// GetValueFromS3 looks up external values for an Expression when the S3 protocol is specified\nfunc (v StandardValueSource) GetValueFromS3(bucket string, key string) (string, error) {\n\tregion, err := getBucketRegion(bucket)\n\tif err != nil {\n\t\tmessage := fmt.Sprintf(\"Cannot get region for bucket %s: %s\", bucket, err.Error())\n\t\treturn \"\", errors.New(message)\n\t}\n\n\tconfig := &aws.Config{Region: aws.String(region)}\n\tawsSession := session.New()\n\ts3Client := s3.New(awsSession, config)\n\tresponse, err := s3Client.GetObject(&s3.GetObjectInput{\n\t\tBucket: aws.String(bucket),\n\t\tKey:    aws.String(key),\n\t})\n\tif err != nil {\n\t\tmessage := fmt.Sprintf(\"Cannot read bucket %s key %s: %s\", bucket, key, err.Error())\n\t\treturn \"\", errors.New(message)\n\t}\n\tbuf := new(bytes.Buffer)\n\tbuf.ReadFrom(response.Body)\n\tvalue := strings.TrimSpace(buf.String())\n\tDebugf(\"Value from bucket %s key %s in region %s: %s\\n\", bucket, key, region, value)\n\treturn value, nil\n}\n\nfunc getBucketRegion(bucket string) (string, error) {\n\tawsSession := session.New()\n\ts3Client := s3.New(awsSession)\n\tlocation, err := s3Client.GetBucketLocation(&s3.GetBucketLocationInput{\n\t\tBucket: aws.String(bucket),\n\t})\n\tif err != nil {\n\t\treturn \"us-east-1\", err\n\t}\n\tif location.LocationConstraint == nil {\n\t\t// default region is us-east-1\n\t\treturn \"us-east-1\", nil\n\t}\n\treturn *location.LocationConstraint, nil\n}\n\n// GetValueFromHTTP looks up external value for an Expression when the HTTP protocol is specified\nfunc (v StandardValueSource) GetValueFromHTTP(url string) (string, error) {\n\thttpResponse, err := http.Get(url)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif httpResponse.StatusCode != 200 {\n\t\treturn \"\", err\n\t}\n\tdefer httpResponse.Body.Close()\n\tbody, err := ioutil.ReadAll(httpResponse.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn strings.TrimSpace(string(body)), nil\n}\n"
  },
  {
    "path": "assertion/value_test.go",
    "content": "package assertion\n\nimport (\n\t\"fmt\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"testing\"\n)\n\nfunc TestCommandLineVariable(t *testing.T) {\n\ts := StandardValueSource{\n\t\tVariables: map[string]string{\"foo\": \"bar\"},\n\t}\n\te := Expression{\n\t\tValueFrom: ValueFrom{Variable: \"foo\"},\n\t}\n\tv, err := s.GetValue(e)\n\tif err != nil {\n\t\tt.Errorf(\"Expected GetValue to return without error: %v\\n\", err.Error())\n\t}\n\tif v != \"bar\" {\n\t\tt.Errorf(\"Expected GetValue to find variable 'foo' with value 'bar', not '%s'\\n\", v)\n\t}\n}\n\nfunc TestValueFromHttp(t *testing.T) {\n\tcidrBlock := \"0.0.0.0/0\"\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tfmt.Fprintln(w, cidrBlock)\n\t}))\n\tdefer ts.Close()\n\ts := StandardValueSource{}\n\te := Expression{\n\t\tValueFrom: ValueFrom{URL: ts.URL},\n\t}\n\tv, err := s.GetValue(e)\n\tassert.Nil(t, err, \"Expecting GetValue to not return an error\")\n\tassert.Equal(t, cidrBlock, v, \"Expecting CIDR value to be returned\")\n}\n"
  },
  {
    "path": "cli/app.go",
    "content": "package main\n\n//go:generate packr -v\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/gobuffalo/packr\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stelligent/config-lint/linter\"\n)\n\nvar version string\n\ntype (\n\t// LinterOptions for applying rules\n\tLinterOptions struct {\n\t\tTags             []string\n\t\tRuleIDs          []string\n\t\tIgnoreRuleIDs    []string\n\t\tQueryExpression  string\n\t\tSearchExpression string\n\t\tExcludePatterns  []string\n\t\tVariables        map[string]string\n\t\tTerraformParser  string\n\t}\n\n\t// ProfileOptions for default options from a project file\n\tProfileOptions struct {\n\t\tRules                []string\n\t\tIDs                  []string\n\t\tIgnoreIDs            []string `json:\"ignore_ids\"`\n\t\tTags                 []string\n\t\tQuery                string\n\t\tFiles                []string\n\t\tTerraform            bool\n\t\tExceptions           []RuleException\n\t\tVariables            map[string]string\n\t\tExcludePatterns      []string `json:\"exclude\"`\n\t\tExcludeFromFilenames []string `json:\"exclude_from\"`\n\t}\n\n\t// RuleException optional list allowing a project to ignore specific rules for specific resources\n\tRuleException struct {\n\t\tRuleID           string\n\t\tResourceCategory string\n\t\tResourceType     string\n\t\tResourceID       string\n\t\tComments         string\n\t}\n\n\t// CommandLineOptions for collecting options from the command line\n\tCommandLineOptions struct {\n\t\tRulesFilenames        arrayFlags\n\t\tExcludePatterns       arrayFlags\n\t\tExcludeFromFilenames  arrayFlags\n\t\tVariables             arrayFlags\n\t\tTerraformParser       *string\n\t\tProfileFilename       *string\n\t\tTerraformBuiltInRules *bool\n\t\tTags                  *string\n\t\tIds                   *string\n\t\tIgnoreIds             *string\n\t\tQueryExpression       *string\n\t\tVerboseReport         *bool\n\t\tSearchExpression      *string\n\t\tValidate              *bool\n\t\tVersion               *bool\n\t\tDebug                 *bool\n\t\tArgs                  []string\n\t}\n\n\t// ReportWriter formats and displays a ValidationReport\n\tReportWriter interface {\n\t\tWriteReport(assertion.ValidationReport, LinterOptions)\n\t}\n\n\t// DefaultReportWriter writes the report to Stdout\n\tDefaultReportWriter struct {\n\t\tWriter io.Writer\n\t}\n)\n\nfunc main() {\n\n\tcommandLineOptions := getCommandLineOptions()\n\n\tif *commandLineOptions.Version == true {\n\t\tfmt.Println(version)\n\t\treturn\n\t}\n\n\tif *commandLineOptions.Debug == true {\n\t\tassertion.SetDebug(true)\n\t}\n\n\tif *commandLineOptions.Validate {\n\t\texitCode, err := validateRules(commandLineOptions.Args, DefaultReportWriter{Writer: os.Stdout})\n\t\tif err != nil {\n\t\t\tfmt.Println(err.Error())\n\t\t}\n\t\tos.Exit(exitCode)\n\t}\n\n\tprofileOptions, err := loadProfile(*commandLineOptions.ProfileFilename)\n\tif err != nil {\n\t\tfmt.Printf(\"Error loading profile: %v\\n\", err)\n\t\tos.Exit(-1)\n\t}\n\n\trulesFilenames := loadFilenames(commandLineOptions.RulesFilenames, profileOptions.Rules)\n\tconfigFilenames := defaultToCurrentDirectory(loadFilenames(commandLineOptions.Args, profileOptions.Files))\n\tuseTerraformBuiltInRules := *commandLineOptions.TerraformBuiltInRules || profileOptions.Terraform\n\n\tif err != nil {\n\t\tfmt.Printf(\"Unable to load exclude patterns: %s\\n\", err)\n\t\tos.Exit(-1)\n\t}\n\n\tlinterOptions, err := getLinterOptions(commandLineOptions, profileOptions)\n\tif err != nil {\n\t\tfmt.Printf(\"Failed to parse options: %v\\n\", err)\n\t\tos.Exit(-1)\n\t}\n\n\truleSets, err := loadRuleSets(rulesFilenames)\n\tif err != nil {\n\t\tfmt.Printf(\"Failed to load rules: %v\\n\", err)\n\t\tos.Exit(-1)\n\t}\n\t// Same rule set applies to both TerraformBuiltInRules and Terraform11BuiltInRules\n\t// loadBuiltInRuleSet can be called recursively against a directory, as done here,\n\t// or can be called against a single file, as done with lint-rule.yml\n\tif useTerraformBuiltInRules {\n\t\tbuiltInRuleSet, err := loadBuiltInRuleSet(\"terraform/\")\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Failed to load built-in rules for Terraform: %v\\n\", err)\n\t\t\tos.Exit(-1)\n\t\t}\n\t\truleSets = append(ruleSets, builtInRuleSet)\n\t}\n\tif len(ruleSets) == 0 {\n\t\tfmt.Println(\"No rules\")\n\t\tos.Exit(-1)\n\t}\n\n\truleSets = addExceptions(ruleSets, profileOptions.Exceptions)\n\n\tos.Exit(applyRules(ruleSets, configFilenames, linterOptions, DefaultReportWriter{Writer: os.Stdout}))\n}\n\nfunc addExceptions(ruleSets []assertion.RuleSet, exceptions []RuleException) []assertion.RuleSet {\n\tsets := []assertion.RuleSet{}\n\tfor _, ruleSet := range ruleSets {\n\t\tsets = append(sets, addExceptionsToRuleSet(ruleSet, exceptions))\n\t}\n\treturn sets\n}\n\nfunc addExceptionsToRuleSet(ruleSet assertion.RuleSet, exceptions []RuleException) assertion.RuleSet {\n\trules := []assertion.Rule{}\n\tfor _, rule := range ruleSet.Rules {\n\t\tfor _, e := range exceptions {\n\t\t\tif rule.ID == e.RuleID && resourceMatch(rule, e) && categoryMatch(rule, e) {\n\t\t\t\trule.Except = append(rule.Except, e.ResourceID)\n\t\t\t}\n\t\t}\n\t\trules = append(rules, rule)\n\t}\n\truleSet.Rules = rules\n\treturn ruleSet\n}\n\nfunc resourceMatch(rule assertion.Rule, exception RuleException) bool {\n\tif assertion.SliceContains(rule.Resources, exception.ResourceType) || rule.Resource == exception.ResourceType {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc categoryMatch(rule assertion.Rule, exception RuleException) bool {\n\treturn rule.Category == exception.ResourceCategory || exception.ResourceCategory == \"resources\" || rule.Category == \"\"\n}\n\nfunc validateRules(filenames []string, w ReportWriter) (int, error) {\n\tbuiltInRuleSet, err := loadBuiltInRuleSet(\"lint-rules.yml\")\n\tif err != nil {\n\t\treturn -1, err\n\t}\n\truleSets := []assertion.RuleSet{builtInRuleSet}\n\tlinterOptions := LinterOptions{\n\t\tQueryExpression: \"Violations[]\",\n\t}\n\treturn applyRules(ruleSets, filenames, linterOptions, w), nil\n}\n\nfunc loadRuleSets(args arrayFlags) ([]assertion.RuleSet, error) {\n\trulesFilenames := yamlFilesOnly(getFilenames(args))\n\truleSets := []assertion.RuleSet{}\n\tfor _, rulesFilename := range rulesFilenames {\n\t\trulesContent, err := ioutil.ReadFile(rulesFilename)\n\t\tif err != nil {\n\t\t\treturn ruleSets, err\n\t\t}\n\t\truleSet, err := assertion.ParseRules(string(rulesContent))\n\t\tif err != nil {\n\t\t\treturn ruleSets, err\n\t\t}\n\t\truleSet.Source = rulesFilename\n\t\truleSets = append(ruleSets, ruleSet)\n\t}\n\treturn ruleSets, nil\n}\n\nfunc isYamlFile(filename string) bool {\n\tconfigPatterns := []string{\"*yml\", \"*.yaml\"}\n\tmatch, _ := assertion.ShouldIncludeFile(configPatterns, filename)\n\treturn match\n\n}\n\nfunc yamlFilesOnly(filenames []string) []string {\n\tconfigFiles := []string{}\n\tfor _, filename := range filenames {\n\t\tmatch := isYamlFile(filename)\n\t\tif match {\n\t\t\tconfigFiles = append(configFiles, filename)\n\t\t}\n\t}\n\treturn configFiles\n}\n\n// Takes a name of a rule YAML file or a directory containing YAML rules\n// Returns a RuleSet of all rules in that file or directory\nfunc loadBuiltInRuleSet(filename string) (assertion.RuleSet, error) {\n\truleSet := assertion.RuleSet{}\n\tbox := packr.NewBox(\"./assets\")\n\tassertion.Debugf(\"Looking for file %v in Box: %v\\n\", filename, box)\n\n\tvar err error\n\tif isYamlFile(filename) && box.Has(filename) {\n\t\truleSet, err = addRuleSet(ruleSet, box, filename)\n\t\tif err != nil {\n\t\t\tassertion.Debugf(\"Failed to add RuleSet: %v\\n\", err)\n\t\t\treturn assertion.RuleSet{}, err // returns empty rule set\n\t\t}\n\t} else if strings.HasSuffix(filename, \"/\") {\n\t\tfilesInBox := box.List()\n\t\tif len(filesInBox) > 0 {\n\t\t\t// Get each file in that box\n\t\t\tfor _, fileInBox := range filesInBox {\n\t\t\t\t// Check if file is YAML and starts with the folder name\n\t\t\t\tassertion.Debugf(\"Box File: %v\\n\", fileInBox)\n\t\t\t\tif isYamlFile(fileInBox) && strings.HasPrefix(fileInBox, filename) {\n\t\t\t\t\tassertion.Debugf(\"Adding rule set: %v\\n\", fileInBox)\n\t\t\t\t\truleSet, err = addRuleSet(ruleSet, box, fileInBox)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tassertion.Debugf(\"Failed to add RuleSet: %v\\n\", err)\n\t\t\t\t\t\treturn assertion.RuleSet{}, err // returns empty rule set\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\treturn assertion.RuleSet{}, errors.New(\"File or directory doesnt exist\")\n\t}\n\n\treturn ruleSet, nil\n}\n\nfunc addRuleSet(ruleSet assertion.RuleSet, box packr.Box, filename string) (assertion.RuleSet, error) {\n\t// Get RuleSet from file\n\tnewRuleSet, err := getRuleSet(box, filename)\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to get RuleSet: %v\\n\", err)\n\t\treturn assertion.RuleSet{}, err // returns empty rule set\n\t}\n\n\t// Join with existing rule sets\n\truleSet, err = assertion.JoinRuleSets(ruleSet, newRuleSet)\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to join RuleSets: %v\\n\", err)\n\t\treturn assertion.RuleSet{}, err // returns empty rule set\n\t}\n\n\treturn ruleSet, nil\n}\n\n// Given a packr box and rule file in that box,\n// build and return a RuleSet\nfunc getRuleSet(box packr.Box, name string) (assertion.RuleSet, error) {\n\trulesContent, err := box.FindString(name)\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to find filename string in box: %v\\n\", err)\n\t\treturn assertion.RuleSet{}, err\n\t}\n\truleSet, err := assertion.ParseRules(string(rulesContent))\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to parse rules from file: %v\\n\", err)\n\t\treturn assertion.RuleSet{}, err\n\t}\n\treturn ruleSet, nil\n}\n\nfunc applyRules(ruleSets []assertion.RuleSet, args arrayFlags, options LinterOptions, w ReportWriter) int {\n\n\treport := assertion.ValidationReport{\n\t\tViolations:       []assertion.Violation{},\n\t\tFilesScanned:     []string{},\n\t\tResourcesScanned: []assertion.ScannedResource{},\n\t}\n\n\ttfParser := options.TerraformParser\n\tfilenames := excludeFilenames(getFilenames(args), options.ExcludePatterns)\n\tvs := assertion.StandardValueSource{Variables: options.Variables}\n\n\tfor _, ruleSet := range ruleSets {\n\t\tl, err := linter.NewLinter(ruleSet, vs, filenames, tfParser)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t\treturn -1\n\t\t}\n\t\tif l != nil {\n\t\t\tif options.SearchExpression != \"\" {\n\t\t\t\tl.Search(ruleSet, options.SearchExpression, os.Stdout)\n\t\t\t} else {\n\t\t\t\toptions := linter.Options{\n\t\t\t\t\tTags:          options.Tags,\n\t\t\t\t\tRuleIDs:       options.RuleIDs,\n\t\t\t\t\tIgnoreRuleIDs: options.IgnoreRuleIDs,\n\t\t\t\t}\n\t\t\t\tr, err := l.Validate(ruleSet, options)\n\t\t\t\tif err != nil {\n\t\t\t\t\tfmt.Println(\"Validate failed:\", err)\n\t\t\t\t}\n\t\t\t\treport = linter.CombineValidationReports(report, r)\n\t\t\t}\n\t\t}\n\t}\n\tw.WriteReport(report, options)\n\treturn generateExitCode(report)\n}\n\nfunc printReport(w io.Writer, report assertion.ValidationReport, queryExpression string) error {\n\tjsonData, err := json.MarshalIndent(report, \"\", \"  \")\n\tif err != nil {\n\t\treturn err\n\t}\n\tif queryExpression != \"\" {\n\t\tvar data interface{}\n\t\terr = yaml.Unmarshal(jsonData, &data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tv, err := assertion.SearchData(queryExpression, data)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts, err := assertion.JSONStringify(v)\n\t\tif err == nil && s != \"null\" {\n\t\t\tfmt.Fprintln(w, s)\n\t\t}\n\t} else {\n\t\tfmt.Fprintln(w, string(jsonData))\n\t}\n\treturn nil\n}\n\ntype arrayFlags []string\n\nfunc (i *arrayFlags) String() string {\n\tif i != nil {\n\t\treturn strings.Join(*i, \",\")\n\t}\n\treturn \"\"\n}\n\nfunc (i *arrayFlags) Set(value string) error {\n\t*i = append(*i, value)\n\treturn nil\n}\n\nfunc generateExitCode(report assertion.ValidationReport) int {\n\tfor _, v := range report.Violations {\n\t\tif v.Status == \"FAILURE\" {\n\t\t\treturn -1\n\t\t}\n\t}\n\treturn 0\n}\n\nfunc loadFilenames(commandLineFilenames []string, profileFilenames []string) []string {\n\tif len(commandLineFilenames) > 0 {\n\t\treturn commandLineFilenames\n\t}\n\tif len(profileFilenames) > 0 {\n\t\treturn profileFilenames\n\t}\n\treturn []string{}\n}\n\nfunc defaultToCurrentDirectory(filenames []string) []string {\n\tif len(filenames) == 0 {\n\t\treturn []string{\".\"}\n\t}\n\treturn filenames\n}\n\nfunc excludeFilenames(filenames []string, excludePatterns []string) []string {\n\tassertion.Debugf(\"Exclude patterns: %v\\n\", excludePatterns)\n\tfilteredFilenames := []string{}\n\tfor _, filename := range filenames {\n\t\tif !excludeFilename(filename, excludePatterns) {\n\t\t\tfilteredFilenames = append(filteredFilenames, filename)\n\t\t}\n\t}\n\treturn filteredFilenames\n}\n\nfunc excludeFilename(filename string, excludePatterns []string) bool {\n\tfor _, pattern := range excludePatterns {\n\t\tmatch, _ := filepath.Match(pattern, filename)\n\t\tif match {\n\t\t\tassertion.Debugf(\"Excluding file: %s using pattern: %s\\n\", filename, pattern)\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc getFilenames(args []string) []string {\n\tfilenames := []string{}\n\tfor _, arg := range args {\n\t\tif arg == \"-\" {\n\t\t\tfilenames = append(filenames, arg)\n\t\t\tcontinue\n\t\t}\n\t\tfi, err := os.Stat(arg)\n\t\tif err != nil {\n\t\t\t// append as is, error reported later when file cannot be opened\n\t\t\tfilenames = append(filenames, arg)\n\t\t\tcontinue\n\t\t}\n\t\tmode := fi.Mode()\n\t\tif mode.IsDir() {\n\t\t\tfilenames = append(filenames, getFilesInDirectory(arg)...)\n\t\t} else {\n\t\t\tfilenames = append(filenames, arg)\n\t\t}\n\t}\n\treturn filenames\n}\n\nfunc getFilesInDirectory(root string) []string {\n\tdirectoryFiles := []string{}\n\terr := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\tfmt.Printf(\"Error processing %s: %s\\n\", path, err)\n\t\t\treturn err\n\t\t}\n\t\tif !info.IsDir() {\n\t\t\tdirectoryFiles = append(directoryFiles, path)\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tfmt.Printf(\"Error walking directory %s: %s\\n\", root, err)\n\t}\n\treturn directoryFiles\n}\n"
  },
  {
    "path": "cli/app_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n\n\t\"github.com/gobuffalo/packr\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stelligent/config-lint/linter\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestLoadTerraformRules(t *testing.T) {\n\t_, err := loadBuiltInRuleSet(\"terraform/\")\n\tif err != nil {\n\t\tt.Errorf(\"Cannot load built-in Terraform rules\")\n\t}\n}\n\nfunc TestLoadValidateRules(t *testing.T) {\n\t_, err := loadBuiltInRuleSet(\"lint-rules.yml\")\n\tif err != nil {\n\t\tt.Errorf(\"Cannot load built-in rules for -validate option\")\n\t}\n}\n\nfunc TestExcludeAll(t *testing.T) {\n\tfilenames := []string{\"file1.tf\", \"file2.tf\", \"file3.tf\"}\n\tpatterns := []string{\"*.tf\"}\n\tfiltered := excludeFilenames(filenames, patterns)\n\tif len(filtered) != 0 {\n\t\tt.Errorf(\"Expecting all files to be excluded, but files are %v\", filtered)\n\t}\n}\n\nfunc TestExcludeSubdirectory(t *testing.T) {\n\tfilenames := []string{\"file1.tf\", \"foo/bar/secrets/database.yml\"}\n\tpatterns := []string{\"foo/bar/secrets/*\"}\n\tfiltered := excludeFilenames(filenames, patterns)\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"Expecting secrets subdirectory to be excluded, but files are %v\", filtered)\n\t}\n}\n\nfunc TestExcludeOnePattern(t *testing.T) {\n\tfilenames := []string{\"file1.tf\", \"file2.tf\", \"file3.tf\"}\n\tpatterns := []string{\"*1.tf\"}\n\tfiltered := excludeFilenames(filenames, patterns)\n\tif len(filtered) != 2 {\n\t\tt.Errorf(\"Expecting one file to be excluded, but files are %v\", filtered)\n\t}\n}\n\nfunc TestExcludeMultiplePattern(t *testing.T) {\n\tfilenames := []string{\"file1.tf\", \"file2.tf\", \"file3.tf\"}\n\tpatterns := []string{\"*1.tf\", \"*2.tf\"}\n\tfiltered := excludeFilenames(filenames, patterns)\n\tif len(filtered) != 1 {\n\t\tt.Errorf(\"Expecting two files to be excluded, but files are %v\", filtered)\n\t}\n}\n\nfunc TestExcludeFrom(t *testing.T) {\n\texcludeFromFilenames := []string{\"./testdata/exclude-list\"}\n\tpatterns, err := loadExcludePatterns([]string{}, excludeFromFilenames)\n\tif err != nil {\n\t\tt.Errorf(\"Expecting loadExcludePatterns returned error: %s\", err.Error())\n\t}\n\tif len(patterns) != 2 {\n\t\tt.Errorf(\"Expecting to load 2 patterns from excludeFromFilenames, not %v\", patterns)\n\t}\n\tif patterns[0] != \"*1.tf\" {\n\t\tt.Errorf(\"Expecting first pattern from file to be '*1.tf', not '%s'\", patterns[0])\n\t}\n\tif patterns[1] != \"*2.tf\" {\n\t\tt.Errorf(\"Expecting second pattern from file to be '*2.tf', not '%s'\", patterns[1])\n\t}\n}\n\nfunc TestProfileExceptions(t *testing.T) {\n\tfilenames := []string{\"./testdata/terraform.yml\"}\n\truleSets, err := loadRuleSets(filenames)\n\tif err != nil {\n\t\tt.Errorf(\"Expecting loadRuleSets to not return error: %s\", err.Error())\n\t}\n\tprofileExceptions := []RuleException{\n\t\t{\n\t\t\tRuleID:           \"RULE_1\",\n\t\t\tResourceCategory: \"resource\",\n\t\t\tResourceType:     \"aws_instance\",\n\t\t\tComments:         \"Testing\",\n\t\t\tResourceID:       \"my-special-resource\",\n\t\t},\n\t}\n\truleSets = addExceptions(ruleSets, profileExceptions)\n\truleExceptions := ruleSets[0].Rules[0].Except\n\tif len(ruleExceptions) != 1 {\n\t\tt.Errorf(\"Expecting Rule.Except to have one ID: %v\", ruleExceptions)\n\t\treturn\n\t}\n\tid := ruleExceptions[0]\n\tif id != \"my-special-resource\" {\n\t\tt.Errorf(\"Unexpected ResourceID found in Except: %s\", id)\n\t}\n}\n\nfunc TestBuiltRules(t *testing.T) {\n\truleSet, err := loadBuiltInRuleSet(\"lint-rules.yml\")\n\tif err != nil {\n\t\tt.Errorf(\"Expecting loadBuiltInRuleSet to not return error: %s\", err.Error())\n\t}\n\tvs := assertion.StandardValueSource{}\n\n\t// Get all rule files from the assets box\n\tbox := packr.NewBox(\"./assets\")\n\tallFilenames := box.List()\n\tvar filenames []string\n\tfor _, filename := range allFilenames {\n\t\tif isYamlFile(filename) && !isTestCase(filename) {\n\t\t\tfilenames = append(filenames, \"assets/\"+filename)\n\t\t}\n\t}\n\tl, err := linter.NewLinter(ruleSet, vs, filenames, \"\")\n\tif err != nil {\n\t\tt.Errorf(\"Expecting NewLinter to not return error: %s\", err.Error())\n\t}\n\toptions := linter.Options{}\n\treport, err := l.Validate(ruleSet, options)\n\tif err != nil {\n\t\tt.Errorf(\"Expecting Validate to not return error: %s\", err.Error())\n\t}\n\tif len(report.Violations) != 0 {\n\t\tt.Errorf(\"Expecting Validate for built in rules to not report any violations: %v\", report.Violations)\n\t}\n}\n\nfunc TestPrintReport(t *testing.T) {\n\tr := assertion.ValidationReport{}\n\tvar b bytes.Buffer\n\terr := printReport(&b, r, \"\")\n\tassert.Nil(t, err, \"Expecting printReport to run without error\")\n\tassert.Contains(t, b.String(), \"FilesScanned\\\": null\")\n\tassert.Contains(t, b.String(), \"ResourcesScanned\\\": null\")\n\tassert.Contains(t, b.String(), \"Violations\\\": null\")\n}\n\nfunc TestPrintReportWithQueryString(t *testing.T) {\n\tr := assertion.ValidationReport{\n\t\tViolations: []assertion.Violation{\n\t\t\tassertion.Violation{RuleMessage: \"Houston, we have a problem\"},\n\t\t},\n\t}\n\tvar b bytes.Buffer\n\terr := printReport(&b, r, \"Violations[]\")\n\tassert.Nil(t, err, \"Expecting printReport to run without error\")\n\tassert.Contains(t, b.String(), \"RuleMessage\")\n\tassert.NotContains(t, b.String(), \"Violations\")\n\tassert.NotContains(t, b.String(), \"FilesScanned\")\n\tassert.NotContains(t, b.String(), \"ResourcesScanned\")\n}\n\ntype MockReportWriter struct {\n\tReport assertion.ValidationReport\n}\n\nfunc (w MockReportWriter) WriteReport(r assertion.ValidationReport, o LinterOptions) {\n\tw.Report = r\n}\n\nfunc TestApplyRules(t *testing.T) {\n\truleSets := []assertion.RuleSet{\n\t\tassertion.RuleSet{\n\t\t\tType: \"JSON\",\n\t\t},\n\t}\n\targs := arrayFlags{}\n\toptions := LinterOptions{}\n\tw := MockReportWriter{}\n\texitCode := applyRules(ruleSets, args, options, w)\n\tassert.Equal(t, exitCode, 0, \"Expecting applyRules to return 0\")\n\tassert.Empty(t, w.Report.Violations, \"Expecting empty report\")\n}\n\nfunc TestValidateRules(t *testing.T) {\n\tfilenames := []string{\"./testdata/has-properties.yml\"}\n\tw := MockReportWriter{}\n\tvalidateRules(filenames, w)\n\tassert.Empty(t, w.Report.Violations, \"Expecting empty report for validateRules\")\n}\n\nfunc TestResourceMatch(t *testing.T) {\n\ttestRule := []assertion.Rule{\n\t\t{\n\t\t\tID:        \"RULE_1\",\n\t\t\tCategory:  \"resource\",\n\t\t\tResources: []string{\"aws_instance\", \"aws_s3_bucket\"},\n\t\t},\n\t\t{\n\t\t\tID:       \"RULE_2\",\n\t\t\tCategory: \"resource\",\n\t\t\tResource: \"aws_s3_bucket\",\n\t\t},\n\t}\n\tprofileExceptions := []RuleException{\n\t\t{\n\t\t\tRuleID:           \"RULE_1\",\n\t\t\tResourceCategory: \"resource\",\n\t\t\tResourceType:     \"aws_instance\",\n\t\t\tComments:         \"Testing\",\n\t\t\tResourceID:       \"my-special-resource\",\n\t\t},\n\t\t{\n\t\t\tRuleID:           \"RULE_2\",\n\t\t\tResourceCategory: \"resources\",\n\t\t\tResourceType:     \"aws_s3_bucket\",\n\t\t\tComments:         \"Testing\",\n\t\t\tResourceID:       \"my-special-bucket\",\n\t\t},\n\t\t{\n\t\t\tRuleID:           \"RULE_2\",\n\t\t\tResourceCategory: \"resources\",\n\t\t\tResourceType:     \"aws_vpc\",\n\t\t\tComments:         \"Should not match\",\n\t\t\tResourceID:       \"my-vpc\",\n\t\t},\n\t}\n\n\tassert.True(t, resourceMatch(testRule[0], profileExceptions[0]), \"Expecting exception resource to be found in rule resources\")\n\tassert.True(t, resourceMatch(testRule[1], profileExceptions[1]), \"Expecting one to one match with exception resource and rule resource\")\n\tassert.False(t, resourceMatch(testRule[1], profileExceptions[2]), \"Expecting rule and exception to not match\")\n}\n\nfunc TestLoadRuleSetsBadFilename(t *testing.T) {\n\targs := []string{\"no-such-file.yml\"}\n\t_, err := loadRuleSets(args)\n\tassert.NotNil(t, err, \"LoadRuleSet with bad filename should return an error\")\n}\n\nfunc TestLoadRuleSetsParseErrors(t *testing.T) {\n\targs := []string{\"./testdata/syntax-errors.yml\"}\n\t_, err := loadRuleSets(args)\n\tassert.NotNil(t, err, \"Expecting rules file with syntax errors to fail\")\n\tif err != nil {\n\t\tassert.Contains(t, err.Error(), \"error unmarshaling JSON\")\n\t}\n}\n\nfunc TestStdinFilename(t *testing.T) {\n\tfilenames := getFilenames([]string{\"-\"})\n\tassert.Len(t, filenames, 1, \"getFilenames should file 1 file\")\n\tassert.Equal(t, filenames[0], \"-\", \"getFilenames should allow - for stdin\")\n}\n\nfunc TestGetFilenamesUsingDirectory(t *testing.T) {\n\tfilenames := getFilenames([]string{\"./testdata/dirtest\"})\n\tassert.Len(t, filenames, 2)\n\tassert.Equal(t, \"testdata/dirtest/a.yml\", filenames[0])\n\tassert.Equal(t, \"testdata/dirtest/b.yml\", filenames[1])\n}\n\nfunc TestLoadFilenamesFromCommandLine(t *testing.T) {\n\tcommandLineFilenames := []string{\"command.yml\"}\n\tprofileFilenames := []string{\"default.yml\"}\n\tresult := loadFilenames(commandLineFilenames, profileFilenames)\n\tassert.Equal(t, result, commandLineFilenames)\n}\n\nfunc TestLoadFilenamesFromProfile(t *testing.T) {\n\tcommandLineFilenames := []string{}\n\tprofileFilenames := []string{\"default.yml\"}\n\tresult := loadFilenames(commandLineFilenames, profileFilenames)\n\tassert.Equal(t, result, profileFilenames)\n}\n\nfunc TestArrayFlags(t *testing.T) {\n\tvar f arrayFlags\n\tassert.Equal(t, \"\", f.String(), \"Default arrayFlags should return empty string\")\n\tf.Set(\"first\")\n\tf.Set(\"second\")\n\tassert.Equal(t, arrayFlags{\"first\", \"second\"}, f, \"Expecting arrayFlags to have two elements\")\n}\n\nfunc TestLoadBuiltInRuleSetMissing(t *testing.T) {\n\t_, err := loadBuiltInRuleSet(\"missing.yml\")\n\tassert.Contains(t, err.Error(), \"File or directory doesnt exist\", \"loadBuiltInRuleSet should fail for missing file\")\n}\n"
  },
  {
    "path": "cli/assets/lint-rules.yml",
    "content": "---\nversion: 1\ndescription: Rules for config-lint\ntype: LintRules\nfiles:\n  - \"*.yml\"\nrules:\n\n  - id: VALID_TYPE\n    message: Not a valid linter type\n    resource: LintRuleSet\n    severity: FAILURE\n    assertions:\n      - key: type\n        op: in\n        value: Terraform,Terraform12,Kubernetes,LintRules,YAML,JSON,CSV\n\n  - id: VALID_VERSION\n    message: RuleSet must have a supported version\n    resource: LintRuleSet\n    severity: WARNING\n    assertions:\n      - key: version\n        op: eq\n        value: 1\n\n  - id: HAS_RULES\n    message: RuleSet needs at least one rule\n    resource: LintRuleSet\n    severity: WARNING\n    assertions:\n      - key: rules\n        op: not-empty\n\n  - id: YAML_RULES_HAVE_RESOURCES_SECTION\n    message: RuleSet for YAML required resources section\n    resource: LintRuleSet\n    severity: FAILURE\n    conditions:\n      - key: type\n        op: eq\n        value: YAML\n    assertions:\n      - key: resources\n        op: present\n\n  - id: EVERY_RULE_HAS_ID\n    message: Event rule in rule set must have an id\n    resource: LintRuleSet\n    severity: FAILURE\n    assertions:\n      - every:\n          key: rules\n          expressions:\n            - key: id\n              op: present\n\n  - id: ID_PRESENT\n    message: Rule must have an ID\n    resource: LintRule\n    severity: FAILURE\n    assertions:\n      - key: id\n        op: present\n\n  - id: RESOURCE_PRESENT\n    message: Rule must have a resource, resources or except_resources attribute\n    severity: FAILURE\n    resource: LintRule\n    assertions:\n      - or:\n        - key: resource\n          op: present\n        - key: resources\n          op: present\n        - key: except_resources\n          op: present\n    tags:\n      - resource\n\n  - id: ASSERTIONS_OR_INVOKE\n    message: Rule must have assertions or invoke\n    resource: LintRule\n    severity: FAILURE\n    assertions:\n      - or:\n          - key: assertions\n            op: present\n          - key: invoke\n            op: present\n\n  - id: VALID_EXPRESSION\n    message: \"These are mutually exclusive in the same expression: key,or,xor,and,not,every,some,none\"\n    resource: LintRule\n    severity: FAILURE\n    assertions:\n      - every:\n          key: \"assertions[]\"\n          expressions:\n            - xor:\n              - key: \"@\"\n                op: has-properties\n                value: key,op\n              - key: \"@\"\n                op: has-properties\n                value: or\n              - key: \"@\"\n                op: has-properties\n                value: xor\n              - key: \"@\"\n                op: has-properties\n                value: and\n              - key: \"@\"\n                op: has-properties\n                value: not\n              - key: \"@\"\n                op: has-properties\n                value: every\n              - key: \"@\"\n                op: has-properties\n                value: some\n              - key: \"@\"\n                op: has-properties\n                value: none\n              - key: \"@\"\n                op: has-properties\n                value: exactly-one\n"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: API_GW_DOMAIN_SECURITY_POLICY_TLS1_2\n    message: API Gateway domain name must use TLS 1.2\n    resource: aws_api_gateway_domain_name\n    severity: FAILURE\n    assertions:\n      - key: security_policy\n        op: eq\n        value: \"TLS_1_2\"\n    tags:\n      - api_gateway"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/terraform12/security_policy.tf",
    "content": "# Test that an api_gateway_domain_name is using TLS 1.2\n# https://www.terraform.io/docs/providers/aws/r/api_gateway_domain_name.html#security_policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: security_policy is set to TLS 1.2\nresource \"aws_api_gateway_domain_name\" \"api_gw_domain_using_tls1_2\" {\n  domain_name = \"api.example.com\"\n\n  endpoint_configuration {\n    types = [\"REGIONAL\"]\n  }\n\n  security_policy = \"TLS_1_2\"\n}\n\n# FAIL: security_policy is not defined\nresource \"aws_api_gateway_domain_name\" \"api_gw_security_policy_not_set\" {\n  domain_name = \"api.example.com\"\n\n  endpoint_configuration {\n    types = [\"REGIONAL\"]\n  }\n}\n\n# FAIL: security_policy is set to TLS 1.0 \nresource \"aws_api_gateway_domain_name\" \"api_gw_domain_using_tls1_0\" {\n  domain_name = \"api.example.com\"\n\n  endpoint_configuration {\n    types = [\"REGIONAL\"]\n  }\n\n  security_policy = \"TLS_1_0\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/api_gateway/api_gateway_domain_name/security_policy/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: API_GW_DOMAIN_SECURITY_POLICY_TLS1_2\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: BATCH_JOB_AWS_ENVIRONMENT_SECRETS\n    message: Environment for batch jobs should not include AWS secrets\n    resource: aws_batch_job_definition\n    severity: FAILURE\n    # This rule fails if it finds a regex match for either the Access Key ID and/or the Secret Access Key\n    assertions:\n      - not:\n        - some:\n            key: \"container_properties.environment[].value\"\n            expressions:\n              # Check if the string starts with any known 4 character ACCESS_KEY sequence\n              # and is 20 capital alpha-numeric characters long in total\n              - key: \"@\"\n                op: regex\n                value: \"^(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}$\"\n        - some:\n            key: \"container_properties.environment[].value\"\n            expressions:\n              - and:\n                # Check if the string is exactly 40 characters long\n                - key: \"@\"\n                  op: regex\n                  value: \"^.{40}$\"\n                # Check if the string contains only alpha-numeric-slash-plus characters with at least 1 / or +\n                - key: \"@\"\n                  op: regex\n                  value: \"^[a-zA-Z0-9/+]+[/+]+[a-zA-Z0-9/+]+$\"\n    tags:\n      - batch\n"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/terraform12/aws_secrets.tf",
    "content": "# Test that AWS secrets are not being used in batch environment variables\n# https://www.terraform.io/docs/providers/aws/r/batch_job_definition.html#container_properties\n# Reference API for container_properties spec: https://docs.aws.amazon.com/batch/latest/APIReference/API_RegisterJobDefinition.html\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: AWS secrets are not used in the env vars\nresource \"aws_batch_job_definition\" \"batch_job_without_secrets\" {\n  name = \"tf_test_batch_job_definition\"\n  type = \"container\"\n\n  container_properties = <<CONTAINER_PROPERTIES\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"},\n        {\"name\": \"VARNAMETWO\", \"value\": \"VARVALTWO\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nCONTAINER_PROPERTIES\n}\n\n# FAIL: AWS secret key in env vars\nresource \"aws_batch_job_definition\" \"batch_job_with_secret_key\" {\n  name = \"tf_test_batch_job_definition\"\n  type = \"container\"\n\n  container_properties = <<CONTAINER_PROPERTIES\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"},\n        {\"name\": \"VARNAMESECRET\", \"value\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nCONTAINER_PROPERTIES\n}\n\n# FAIL: AWS access key ID in env vars\nresource \"aws_batch_job_definition\" \"batch_job_with_secret_access_id\" {\n  name = \"tf_test_batch_job_definition\"\n  type = \"container\"\n\n  container_properties = <<CONTAINER_PROPERTIES\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"},\n        {\"name\": \"VARNAMESECRET\", \"value\": \"AKIAIOSFODNN7EXAMPLE\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nCONTAINER_PROPERTIES\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/aws_secrets/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 test\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: BATCH_JOB_AWS_ENVIRONMENT_SECRETS\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: BATCH_DEFINITION_PRIVILEGED\n    message: Batch Job Definition Container Properties should not have Privileged set to true\n    resource: aws_batch_job_definition\n    severity: WARNING\n    assertions:\n      - not:\n          - key: container_properties.privileged\n            op: is-true\n    tags:\n      - batch\n"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform11/container_properties_privileged.tf",
    "content": "# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_not_set\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_set_to_false\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"privileged\": \"false\",\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_batch_job_definition\" \"container_properties_privileged_set_to_true\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"privileged\": \"true\",\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/terraform12/container_properties_privileged.tf",
    "content": "# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_not_set\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_batch_job_definition\" \"container_properties_privileged_set_to_false\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"privileged\": \"false\",\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_batch_job_definition\" \"container_properties_privileged_set_to_true\" {\n  name = \"foo\"\n  type = \"container\"\n\n  container_properties = <<EOF\n{\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"privileged\": \"true\",\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: BATCH_DEFINITION_PRIVILEGED\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_DISTRIBUTION_ORIGIN_POLICY\n    message: CloudFront Distribution should use OAI or origin_protocol_policy should be https-only\n    resource: aws_cloudfront_distribution\n    severity: FAILURE\n    assertions:\n      - or:\n        - key: \"origin[].s3_origin_config[].origin_access_identity\"\n          op: present\n        - or:\n          - key: \"origin[].custom_origin_config\"\n            op: absent\n          - key: \"origin[].custom_origin_config[].origin_protocol_policy\"\n            op: contains\n            value: https-only\n    tags:\n      - cloudfront\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform11/custom_origin_config.tf",
    "content": "# Pass\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_not_set\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"https-only\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_http-only\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"http-only\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_match-viewer\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"match-viewer\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/terraform12/custom_origin_config.tf",
    "content": "# Test that a cloudfront_distribution resource is using OAI or origin_protocol_policy is using https-only\n# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin_access_identity\n# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin_protocol_policy\n\n## Setup Helper\nvariable \"test_domain_s3_location\" {\n  default = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n}\n\nvariable \"test_origin_id\" {\n  default = \"fooOrigin\"\n}\n\nvariable \"test_logging_bucket\" {\n  default = \"foologs.s3.amazonaws.com\"\n}\n\nvariable \"test_logging_prefix\" {\n  default = \"aws_cloudfront_distribution\"\n}\n\n# PASS: OIA is used\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_not_set\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# PASS: origin_protocol_policy is https-only\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"https-only\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# FAIL: origin_protocol_policy is not https-only\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_http-only\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"http-only\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# FAIL: origin_protocol_policy is not https-only\nresource \"aws_cloudfront_distribution\" \"custom_origin_config_set_to_match-viewer\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    custom_origin_config {\n      http_port              = 80\n      https_port             = 443\n      origin_protocol_policy = \"match-viewer\"\n      origin_ssl_protocols   = [\"TLSv1.2\"]\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/custom_origin_config/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDFRONT_DISTRIBUTION_ORIGIN_POLICY\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_DISTRIBUTION_LOGGING\n    message: CloudFront Distribution must configure logging\n    resource: aws_cloudfront_distribution\n    severity: FAILURE\n    assertions:\n      - key: logging_config\n        op: present\n    tags:\n      - cloudfront\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform11/logging_config.tf",
    "content": "# Pass\nresource \"aws_cloudfront_distribution\" \"logging_enabled\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"logging_disabled\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/terraform12/logging_config.tf",
    "content": "## Setup Helper\nvariable \"test_domain_s3_location\" {\n  default = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n}\n\nvariable \"test_origin_id\" {\n  default = \"fooOrigin\"\n}\n\nvariable \"test_logging_bucket\" {\n  default = \"foologs.s3.amazonaws.com\"\n}\n\nvariable \"test_logging_prefix\" {\n  default = \"aws_cloudfront_distribution\"\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"logging_enabled\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"logging_disabled\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/logging_config/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDFRONT_DISTRIBUTION_LOGGING\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_MINIMUM_SSL\n    message: CloudFront Distribution must use TLS 1.2\n    resource: aws_cloudfront_distribution\n    severity: FAILURE\n    assertions:\n      - key: viewer_certificate[].cloudfront_default_certificate | [0]\n        op: is-false\n      - key: viewer_certificate[].minimum_protocol_version | [0]\n        op: starts-with\n        value: \"TLSv1.2\"\n    tags:\n      - cloudfront\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/terraform12/minimum_ssl_protocol.tf",
    "content": "# Test that a CloudFront distribution viewer_certificate is using TLS 1.2\n# https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#viewer-certificate-arguments\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS\nresource \"aws_cloudfront_distribution\" \"cf_using_tls_1_2\" {\n  origin {\n    domain_name = \"example.com\"\n    origin_id   = \"s3ExampleOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  enabled             = true\n  is_ipv6_enabled     = true\n  comment             = \"Some comment\"\n  default_root_object = \"index.html\"\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"mylogs.s3.amazonaws.com\"\n    prefix          = \"myprefix\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"s3ExampleOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = false\n    minimum_protocol_version       = \"TLSv1.2_2018\"\n  }\n}\n\n# FAIL: distribution has cloudfront_default_certificate enabled\nresource \"aws_cloudfront_distribution\" \"cf_using_tls_1_2_with_default_cert\" {\n  origin {\n    domain_name = \"example.com\"\n    origin_id   = \"s3ExampleOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  enabled             = true\n  is_ipv6_enabled     = true\n  comment             = \"Some comment\"\n  default_root_object = \"index.html\"\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"mylogs.s3.amazonaws.com\"\n    prefix          = \"myprefix\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"s3ExampleOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n    minimum_protocol_version       = \"TLSv1.2_2018\"\n  }\n}\n\n# FAIL: distribution is not using TLS 1.2\nresource \"aws_cloudfront_distribution\" \"cf_using_not_using_tls_1_2\" {\n  origin {\n    domain_name = \"example.com\"\n    origin_id   = \"s3ExampleOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  enabled             = true\n  is_ipv6_enabled     = true\n  comment             = \"Some comment\"\n  default_root_object = \"index.html\"\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"mylogs.s3.amazonaws.com\"\n    prefix          = \"myprefix\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"s3ExampleOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = false\n    minimum_protocol_version       = \"TLSv1\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/minimum_ssl_protocol/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDFRONT_MINIMUM_SSL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDFRONT_DISTRIBUTION_PROTOCOL\n    message: CloudFront Distribution should not allow all protocols\n    resource: aws_cloudfront_distribution\n    severity: FAILURE\n    assertions:\n      - key: \"default_cache_behavior[].viewer_protocol_policy\"\n        op: does-not-contain\n        value: allow-all\n      - key: \"ordered_cache_behavior[].viewer_protocol_policy\"\n        op: does-not-contain\n        value: allow-all\n    tags:\n      - cloudfront\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform11/viewer_protocol_policy.tf",
    "content": "# Pass\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"https-only\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_allow-all\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"https-only\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"redirect-to-https\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_allow-all\" {\n  enabled = true\n\n  origin {\n    domain_name = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n    origin_id   = \"fooOrigin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"foologs.s3.amazonaws.com\"\n    prefix          = \"aws_cloudfront_distribution\"\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"allow-all\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/terraform12/viewer_protocol_policy.tf",
    "content": "## Setup Helper\nvariable \"test_domain_s3_location\" {\n  default = \"http://foo.s3-website-us-east-1.amazonaws.com\"\n}\n\nvariable \"test_origin_id\" {\n  default = \"fooOrigin\"\n}\n\nvariable \"test_logging_bucket\" {\n  default = \"foologs.s3.amazonaws.com\"\n}\n\nvariable \"test_logging_prefix\" {\n  default = \"aws_cloudfront_distribution\"\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"https-only\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"default_cache_behavior_viewer_protocol_policy_set_to_allow-all\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_https-only\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"https-only\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Pass\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_redirect-to-https\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"redirect-to-https\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n\n# Fail\nresource \"aws_cloudfront_distribution\" \"ordered_cache_behavior_viewer_protocol_policy_set_to_allow-all\" {\n  enabled = true\n\n  origin {\n    domain_name = var.test_domain_s3_location\n    origin_id   = var.test_origin_id\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  logging_config {\n    include_cookies = false\n    bucket          = var.test_logging_bucket\n    prefix          = var.test_logging_prefix\n  }\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"redirect-to-https\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  ordered_cache_behavior {\n    path_pattern     = \"/foo/bar/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"fooOrigin\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"allow-all\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudfront/cloudfront_distribution/viewer_protocol_policy/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDFRONT_DISTRIBUTION_PROTOCOL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDTRAIL_ENCRYPTION\n    message: CloudTrail should specify a non-default KMS Key\n    resource: aws_cloudtrail\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - cloudtrail\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform11/kms_key_id.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.test_key.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_cloudtrail\" \"kms_key_id_is_set\" {\n  name           = \"foo\"\n  s3_bucket_name = \"${aws_s3_bucket.test_bucket.bucket}\"\n  kms_key_id     = \"${aws_kms_key.test_key.arn}\"\n}\n\n# Warn\nresource \"aws_cloudtrail\" \"kms_key_id_is_not_set\" {\n  name           = \"foo\"\n  s3_bucket_name = \"${aws_s3_bucket.test_bucket.bucket}\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/terraform12/kms_key_id.tf",
    "content": "## Setup Helper\nvariable \"test_cloudtrail_name\" {\n  default = \"foo\"\n}\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.test_key.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_cloudtrail\" \"kms_key_id_is_set\" {\n  name           = var.test_cloudtrail_name\n  s3_bucket_name = aws_s3_bucket.test_bucket.bucket\n  kms_key_id     = aws_kms_key.test_key.arn\n}\n\n# Warn\nresource \"aws_cloudtrail\" \"kms_key_id_is_not_set\" {\n  name           = var.test_cloudtrail_name\n  s3_bucket_name = aws_s3_bucket.test_bucket.bucket\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudtrail/cloudtrail/kms_key_id/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDTRAIL_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CLOUDWATCH_WILDCARD_PRINCIPAL\n    message: Cloudwatch destination policy allow policy should not use a wildcard princpal\n    resource: aws_cloudwatch_log_destination_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: access_policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - cloudwatch\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "content": "# Test that CloudWatch log destination policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/cloudwatch_log_destination_policy.html#access_policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Allow statement does not use a wildcard principal\nresource \"aws_cloudwatch_log_destination_policy\" \"cw_destination_no_wildcard\" {\n  destination_name = \"cloudwatch_destination\"\n  access_policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"cloudwatch:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny statement does not use a wildcard principal\nresource \"aws_cloudwatch_log_destination_policy\" \"cw_destination_deny_no_wildcard\" {\n  destination_name = \"cloudwatch_destination\"\n  access_policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"cloudwatch:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny statement uses a wildcard principal\nresource \"aws_cloudwatch_log_destination_policy\" \"cw_destination_deny_with_wildcard\" {\n  destination_name = \"cloudwatch_destination\"\n  access_policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"cloudwatch:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow statement uses a wildcard principal\nresource \"aws_cloudwatch_log_destination_policy\" \"cw_destination_allow_with_wildcard\" {\n  destination_name = \"cloudwatch_destination\"\n  access_policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"cloudwatch:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow statement uses a wildcard principal\nresource \"aws_cloudwatch_log_destination_policy\" \"cw_destination_principal_is_wildcard\" {\n  destination_name = \"cloudwatch_destination\"\n  access_policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"cloudwatch:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:logs:us-west-1:123456789012:log-group:/mystack-testgroup-12ABC1AB12A1:*\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/cloudwatch/cloudwatch_log_destination_policy/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CLOUDWATCH_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEBUILD_PROJECT_ARTIFACT_ENCRYPTION\n    message: CodeBuild Project artifacts should be encrypted\n    resource: aws_codebuild_project\n    severity: FAILURE\n    assertions:\n      - not:  \n        - every: # artifacts are required; encryption must be enabled\n            key: artifacts[]\n            expressions:\n              - key: encryption_disabled\n                op: is-true\n        - and: # If 2nd artifacts exist, encryption must be enabled\n          - key: secondary_artifacts\n            op: present\n          - every:\n              key: secondary_artifacts[]\n              expressions:\n                - key: encryption_disabled\n                  op: is-true\n        - and: # If s3 logs exist, encryption must be enabled\n          - key: s3_logs \n            op: present\n          - every:\n              key: s3_logs[]\n              expressions:\n                - key: encryption_disabled\n                  op: is-true\n    tags:\n      - codebuild\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/terraform11/artifact_encryption.tf",
    "content": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codebuild.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\n# Resource required for creating project\nresource \"aws_iam_role_policy\" \"build\" {\n  role = \"${aws_iam_role.build.name}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Resource\": [\n        \"*\"\n      ],\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ]\n    }\n  ]\n}\nPOLICY\n}\n\n# project with encryption key, but artifact encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_artifact_encryption\" {\n  name          = \"fail_artifact_encryption_project\"\n  description   = \"fail_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type                = \"S3\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and artifact encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_artifact_encryption\" {\n  name          = \"pass_artifact_encryption_project\"\n  description   = \"pass_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, but secondary artifact encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_secondary_artifact_encryption\" {\n  name          = \"fail_secondary_artifact_encryption_project\"\n  description   = \"fail_secondary_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  secondary_artifacts {\n    type                = \"S3\"\n    artifact_identifier = \"i_am_an_identifier\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and secondary artifact encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_secondary_artifact_encryption\" {\n  name          = \"pass_secondary_artifact_encryption_project\"\n  description   = \"pass_secondary_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  secondary_artifacts {\n    type                = \"S3\"\n    artifact_identifier = \"i_am_an_identifier\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, but S3 encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_s3_encryption\" {\n  name          = \"fail_s3_encryption_project\"\n  description   = \"fail_s3_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  s3_logs {\n    status              = \"ENABLED\"\n    location            = \"iamabucket/path/to/a/location\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and S3 encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_s3_encryption\" {\n  name          = \"pass_s3_encryption_project\"\n  description   = \"pass_s3_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  s3_logs {\n    status   = \"ENABLED\"\n    location = \"iamabucket/path/to/a/location\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/terraform12/artifact_encryption.tf",
    "content": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codebuild.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\n# Resource required for creating project\nresource \"aws_iam_role_policy\" \"build\" {\n  role = aws_iam_role.build.name\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Resource\": [\n        \"*\"\n      ],\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ]\n    }\n  ]\n}\nPOLICY\n}\n\n# project with encryption key, but artifact encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_artifact_encryption\" {\n  name          = \"fail_artifact_encryption_project\"\n  description   = \"fail_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type                = \"S3\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and artifact encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_artifact_encryption\" {\n  name          = \"pass_artifact_encryption_project\"\n  description   = \"pass_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, but secondary artifact encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_secondary_artifact_encryption\" {\n  name          = \"fail_secondary_artifact_encryption_project\"\n  description   = \"fail_secondary_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  secondary_artifacts {\n    type                = \"S3\"\n    artifact_identifier = \"i_am_an_identifier\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and secondary artifact encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_secondary_artifact_encryption\" {\n  name          = \"pass_secondary_artifact_encryption_project\"\n  description   = \"pass_secondary_artifact_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  secondary_artifacts {\n    type                = \"S3\"\n    artifact_identifier = \"i_am_an_identifier\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, but S3 encryption is disabled. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_s3_encryption\" {\n  name          = \"fail_s3_encryption_project\"\n  description   = \"fail_s3_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  s3_logs {\n    status              = \"ENABLED\"\n    location            = \"iamabucket/path/to/a/location\"\n    encryption_disabled = true\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project with encryption key, and S3 encryption is not disabled. \n# Should pass\nresource \"aws_codebuild_project\" \"pass_s3_encryption\" {\n  name          = \"pass_s3_encryption_project\"\n  description   = \"pass_s3_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"S3\"\n  }\n\n  s3_logs {\n    status   = \"ENABLED\"\n    location = \"iamabucket/path/to/a/location\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/artifact_encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CODEBUILD_PROJECT_ARTIFACT_ENCRYPTION \n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEBUILD_PROJECT_ENCRYPTION\n    message: CodeBuild Project should be encrypted\n    resource: aws_codebuild_project\n    severity: FAILURE\n    assertions:\n      - key: encryption_key\n        op: present\n    tags:\n      - codebuild\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/terraform11/project_encryption.tf",
    "content": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codebuild.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\n# Resource required for creating project\nresource \"aws_iam_role_policy\" \"build\" {\n  role = \"${aws_iam_role.build.name}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Resource\": [\n        \"*\"\n      ],\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ]\n    }\n  ]\n}\nPOLICY\n}\n\n# project with encryption. \n# Should Pass\nresource \"aws_codebuild_project\" \"pass_encryption\" {\n  name          = \"pass_encryption_project\"\n  description   = \"pass_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"NO_ARTIFACTS\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project without encryption. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_encryption\" {\n  name          = \"fail_encryption_project\"\n  description   = \"fail_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = \"${aws_iam_role.build.arn}\"\n\n  artifacts {\n    type = \"NO_ARTIFACTS\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/terraform12/project_encryption.tf",
    "content": "# Resource required for creating project\nresource \"aws_iam_role\" \"build\" {\n  name = \"build\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codebuild.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\n# Resource required for creating project\nresource \"aws_iam_role_policy\" \"build\" {\n  role = aws_iam_role.build.name\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Resource\": [\n        \"*\"\n      ],\n      \"Action\": [\n        \"logs:CreateLogGroup\",\n        \"logs:CreateLogStream\",\n        \"logs:PutLogEvents\"\n      ]\n    }\n  ]\n}\nPOLICY\n}\n\n# project with encryption. \n# Should Pass\nresource \"aws_codebuild_project\" \"pass_encryption\" {\n  name          = \"pass_encryption_project\"\n  description   = \"pass_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"NO_ARTIFACTS\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n\n  encryption_key = \"iamanencryptionkey\"\n}\n\n# project without encryption. \n# Should fail\nresource \"aws_codebuild_project\" \"fail_encryption\" {\n  name          = \"fail_encryption_project\"\n  description   = \"fail_encryption_project\"\n  build_timeout = \"5\"\n  service_role  = aws_iam_role.build.arn\n\n  artifacts {\n    type = \"NO_ARTIFACTS\"\n  }\n\n  environment {\n    compute_type = \"BUILD_GENERAL1_SMALL\"\n    image        = \"aws/codebuild/nodejs:6.3.1\"\n    type         = \"LINUX_CONTAINER\"\n  }\n\n  source {\n    type     = \"GITHUB\"\n    location = \"https://gist.github.com/blahblahblah.git\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/codebuild/codebuild_project/project_encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CODEBUILD_PROJECT_ENCRYPTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: CODEPIPELINE_ENCRYPTION\n    message: CodePipeline should specify a non-default KMS Key to encrypt artifacts\n    resource: aws_codepipeline\n    severity: WARNING\n    assertions:\n      - every:\n          key: artifact_store\n          expressions:\n            - key: encryption_key\n              op: present\n    tags:\n      - codepipeline\n"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/terraform11/encryption_key.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codepipeline.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_role_policy\" \"test_policy\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\":\"Allow\",\n      \"Action\": [\n        \"s3:GetObject\",\n        \"s3:GetObjectVersion\",\n        \"s3:GetBucketVersioning\",\n        \"s3:PutObject\"\n      ],\n      \"Resource\": [\n        \"${aws_s3_bucket.test_bucket.arn}\",\n        \"${aws_s3_bucket.test_bucket.arn}/*\"\n      ]\n    },\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"codebuild:BatchGetBuilds\",\n        \"codebuild:StartBuild\"\n      ],\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\ndata \"aws_kms_alias\" \"test_key\" {\n  name = \"alias/foo\"\n}\n\n# Pass\nresource \"aws_codepipeline\" \"artifact_store_encryption_key_is_set\" {\n  name     = \"foo\"\n  role_arn = \"${aws_iam_role.test_role.arn}\"\n\n  artifact_store {\n    location = \"${aws_s3_bucket.test_bucket.bucket}\"\n    type     = \"S3\"\n\n    encryption_key {\n      id   = \"${data.aws_kms_alias.test_key.arn}\"\n      type = \"KMS\"\n    }\n  }\n\n  stage {\n    name = \"Source\"\n\n    action {\n      name             = \"Source\"\n      category         = \"Source\"\n      owner            = \"ThirdParty\"\n      provider         = \"GitHub\"\n      version          = \"1\"\n      output_artifacts = [\"source_output\"]\n\n      configuration = {\n        Owner  = \"foo\"\n        Repo   = \"bar\"\n        Branch = \"master\"\n      }\n    }\n  }\n\n  stage {\n    name = \"Build\"\n\n    action {\n      name             = \"Build\"\n      category         = \"Build\"\n      owner            = \"AWS\"\n      provider         = \"CodeBuild\"\n      input_artifacts  = [\"source_output\"]\n      output_artifacts = [\"build_output\"]\n      version          = \"1\"\n\n      configuration = {\n        ProjectName = \"test\"\n      }\n    }\n  }\n}\n\n# Warn\nresource \"aws_codepipeline\" \"artifact_store_encryption_key_is_not_set\" {\n  name     = \"foo\"\n  role_arn = \"${aws_iam_role.test_role.arn}\"\n\n  artifact_store {\n    location = \"${aws_s3_bucket.test_bucket.bucket}\"\n    type     = \"S3\"\n  }\n\n  stage {\n    name = \"Source\"\n\n    action {\n      name             = \"Source\"\n      category         = \"Source\"\n      owner            = \"ThirdParty\"\n      provider         = \"GitHub\"\n      version          = \"1\"\n      output_artifacts = [\"source_output\"]\n\n      configuration = {\n        Owner  = \"foo\"\n        Repo   = \"bar\"\n        Branch = \"master\"\n      }\n    }\n  }\n\n  stage {\n    name = \"Build\"\n\n    action {\n      name             = \"Build\"\n      category         = \"Build\"\n      owner            = \"AWS\"\n      provider         = \"CodeBuild\"\n      input_artifacts  = [\"source_output\"]\n      output_artifacts = [\"build_output\"]\n      version          = \"1\"\n\n      configuration = {\n        ProjectName = \"test\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/terraform12/encryption_key.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Service\": \"codepipeline.amazonaws.com\"\n      },\n      \"Action\": \"sts:AssumeRole\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_role_policy\" \"test_policy\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\":\"Allow\",\n      \"Action\": [\n        \"s3:GetObject\",\n        \"s3:GetObjectVersion\",\n        \"s3:GetBucketVersioning\",\n        \"s3:PutObject\"\n      ],\n      \"Resource\": [\n        \"${aws_s3_bucket.test_bucket.arn}\",\n        \"${aws_s3_bucket.test_bucket.arn}/*\"\n      ]\n    },\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"codebuild:BatchGetBuilds\",\n        \"codebuild:StartBuild\"\n      ],\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\ndata \"aws_kms_alias\" \"test_key\" {\n  name = \"alias/foo\"\n}\n\n# Pass\nresource \"aws_codepipeline\" \"artifact_store_encryption_key_is_set\" {\n  name     = \"foo\"\n  role_arn = aws_iam_role.test_role.arn\n\n  artifact_store {\n    location = aws_s3_bucket.test_bucket.bucket\n    type     = \"S3\"\n\n    encryption_key {\n      id   = data.aws_kms_alias.test_key.arn\n      type = \"KMS\"\n    }\n  }\n\n  stage {\n    name = \"Source\"\n\n    action {\n      name             = \"Source\"\n      category         = \"Source\"\n      owner            = \"ThirdParty\"\n      provider         = \"GitHub\"\n      version          = \"1\"\n      output_artifacts = [\"source_output\"]\n\n      configuration = {\n        Owner  = \"foo\"\n        Repo   = \"bar\"\n        Branch = \"master\"\n      }\n    }\n  }\n\n  stage {\n    name = \"Build\"\n\n    action {\n      name             = \"Build\"\n      category         = \"Build\"\n      owner            = \"AWS\"\n      provider         = \"CodeBuild\"\n      input_artifacts  = [\"source_output\"]\n      output_artifacts = [\"build_output\"]\n      version          = \"1\"\n\n      configuration = {\n        ProjectName = \"test\"\n      }\n    }\n  }\n}\n\n# Warn\nresource \"aws_codepipeline\" \"artifact_store_encryption_key_is_not_set\" {\n  name     = \"foo\"\n  role_arn = aws_iam_role.test_role.arn\n\n  artifact_store {\n    location = aws_s3_bucket.test_bucket.bucket\n    type     = \"S3\"\n  }\n\n  stage {\n    name = \"Source\"\n\n    action {\n      name             = \"Source\"\n      category         = \"Source\"\n      owner            = \"ThirdParty\"\n      provider         = \"GitHub\"\n      version          = \"1\"\n      output_artifacts = [\"source_output\"]\n\n      configuration = {\n        Owner  = \"foo\"\n        Repo   = \"bar\"\n        Branch = \"master\"\n      }\n    }\n  }\n\n  stage {\n    name = \"Build\"\n\n    action {\n      name             = \"Build\"\n      category         = \"Build\"\n      owner            = \"AWS\"\n      provider         = \"CodeBuild\"\n      input_artifacts  = [\"source_output\"]\n      output_artifacts = [\"build_output\"]\n      version          = \"1\"\n\n      configuration = {\n        ProjectName = \"test\"\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/codepipeline/codepipeline/encryption_key/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: CODEPIPELINE_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AWS_DMS_ENDPOINT_ENCRYPTION\n    message: AWS DMS Endpoint should have a kms key present\n    resource: aws_dms_endpoint\n    severity: WARNING\n    assertions:\n      - key: kms_key_arn\n        op: present\n    tags:\n      - dms\n"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/terraform11/kms_key.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_dms_endpoint\" \"kms_key_arn_is_set\" {\n  endpoint_id   = \"foo\"\n  endpoint_type = \"source\"\n  engine_name   = \"aurora\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n}\n\n# Warn\nresource \"aws_dms_endpoint\" \"kms_key_arn_is_not_set\" {\n  endpoint_id   = \"foo\"\n  endpoint_type = \"source\"\n  engine_name   = \"aurora\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/terraform12/kms_key.tf",
    "content": "## Setup Helper\nvariable \"test_id\" {\n  default = \"foo\"\n}\n\nvariable \"test_engine_type\" {\n  default = \"source\"\n}\n\nvariable \"test_engine_name\" {\n  default = \"aurora\"\n}\n\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_dms_endpoint\" \"kms_key_arn_is_set\" {\n  endpoint_id   = var.test_id\n  endpoint_type = var.test_engine_type\n  engine_name   = var.test_engine_name\n  kms_key_arn   = aws_kms_key.test_key.arn\n}\n\n# Warn\nresource \"aws_dms_endpoint\" \"kms_key_arn_is_not_set\" {\n  endpoint_id   = var.test_id\n  endpoint_type = var.test_engine_type\n  engine_name   = var.test_engine_name\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/dms/dms_endpoint/endpoint_kms_key/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: AWS_DMS_ENDPOINT_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: DOCUMENTDB_AUDIT_LOGS\n    message: DocumentDB should have audit logging enabled\n    resource: aws_docdb_cluster\n    severity: FAILURE\n    assertions:\n      - key: enabled_cloudwatch_logs_exports\n        op: contains\n        value: \"audit\"\n    tags:\n      - documentdb"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/tests/terraform12/audit_logs.tf",
    "content": "# Test that DocumentDB audit logging is enabled\n# https://www.terraform.io/docs/providers/aws/r/docdb_cluster.html#enabled_cloudwatch_logs_exports\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: enabled_cloudwatch_logs_exports defined with audit logging\nresource \"aws_docdb_cluster\" \"docdb_audit_logging_enabled\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n\n  enabled_cloudwatch_logs_exports = [\"audit\"]\n}\n\n# FAIL: enabled_cloudwatch_logs_exports not defined\nresource \"aws_docdb_cluster\" \"docdb_cloudwatch_logs_exports_not_defined\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n}\n\n# FAIL: enabled_cloudwatch_logs_exports defined without audit logging\nresource \"aws_docdb_cluster\" \"docdb_cloudwatch_logs_exports_without_audit\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n\n  enabled_cloudwatch_logs_exports = [\"profiler\"]\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/audit_logs/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: DOCUMENTDB_AUDIT_LOGS\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: DOCUMENTDB_STORAGE_ENCRYPTION\n    message: DocumentDB should have storage encryption enabled\n    resource: aws_docdb_cluster\n    severity: FAILURE\n    assertions:\n      - key: storage_encrypted\n        op: is-true\n    tags:\n      - documentdb\n\n  - id: DOCUMENTDB_STORAGE_KMS\n    message: DocumentDB should have a KMS key specified\n    resource: aws_docdb_cluster\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - documentdb"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/tests/terraform12/storage_encryption.tf",
    "content": "# Test that a DocumentDB cluster has encryption enabled and a KMS key\n# https://www.terraform.io/docs/providers/aws/r/docdb_cluster.html#storage_encrypted\n# https://www.terraform.io/docs/providers/aws/r/docdb_cluster.html#kms_key_id\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: storage_encrypted defined and set to true\nresource \"aws_docdb_cluster\" \"docdb_storage_encrypted_true\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n  storage_encrypted       = true\n  kms_key_id              = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# WARN: kms_key_id is not provided\nresource \"aws_docdb_cluster\" \"docdb_storage_encrypted_true_no_kms\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n  storage_encrypted       = true\n}\n\n# FAIL: storage_encrypted not defined\n# WARN: kms_key_id is not provided\nresource \"aws_docdb_cluster\" \"docdb_storage_encrypted_not_defined\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n}\n\n# FAIL: storage_encrypted set to false\n# WARN: kms_key_id is not provided\nresource \"aws_docdb_cluster\" \"docdb_storage_encrypted_false\" {\n  cluster_identifier      = \"my-docdb-cluster\"\n  engine                  = \"docdb\"\n  master_username         = \"foo\"\n  master_password         = \"mustbeeightchars\"\n  backup_retention_period = 5\n  preferred_backup_window = \"07:00-09:00\"\n  skip_final_snapshot     = true\n  storage_encrypted       = false\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/documentdb/docdb_cluster/storage_encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: DOCUMENTDB_STORAGE_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: DOCUMENTDB_STORAGE_KMS\n    warnings: 3\n    failures: 0\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AMI_VOLUMES_ENCRYPTED\n    message: Volumes should be encrypted for aws_ami\n    resource: aws_ami\n    severity: FAILURE\n    assertions:\n      - every:\n         key: ebs_block_device\n         expressions:\n           - key: encrypted\n             op: is-true\n    tags:\n      - ami\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/terraform11/ebs_block_device_encrypted.tf",
    "content": "# Pass\nresource \"aws_ami\" \"ebs_block_device_encrypted_set_to_true\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 8\n    encrypted   = true\n  }\n}\n\n# Fail\nresource \"aws_ami\" \"ebs_block_device_encrypted_set_to_false\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 8\n    encrypted   = false\n  }\n}\n\n# Fail\nresource \"aws_ami\" \"ebs_block_device_encrypted_not_set\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 8\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/terraform12/ebs_block_device_encrypted.tf",
    "content": "## Setup Helper\nvariable \"test_device\" {\n  default = \"/dev/xvda\"\n}\n\nvariable \"test_volume\" {\n  default = 8\n}\n\n# Pass\nresource \"aws_ami\" \"ebs_block_device_encrypted_set_to_true\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = var.test_device\n    volume_size = var.test_volume\n    encrypted   = true\n  }\n}\n\n# Fail\nresource \"aws_ami\" \"ebs_block_device_encrypted_set_to_false\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = var.test_device\n    volume_size = var.test_volume\n    encrypted   = false\n  }\n}\n\n# Fail\nresource \"aws_ami\" \"ebs_block_device_encrypted_not_set\" {\n  name = \"foo\"\n\n  ebs_block_device {\n    device_name = var.test_device\n    volume_size = var.test_volume\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami/ebs_block_device_encrypted/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: AMI_VOLUMES_ENCRYPTED\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AMI_COPY_SNAPSHOTS_ENCRYPTED\n    message: Destination snapshots should be encrypted for aws_ami_copy\n    resource: aws_ami_copy\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n    tags:\n      - ami\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/terraform11/encrypted.tf",
    "content": "# Pass\nresource \"aws_ami_copy\" \"encrypted_set_to_true\" {\n  name              = \"foo\"\n  source_ami_id     = \"ami-xxxxxxxx\"\n  source_ami_region = \"us-east-1\"\n  encrypted         = true\n}\n\n# Fail\nresource \"aws_ami_copy\" \"encrypted_set_to_false\" {\n  name              = \"foo\"\n  source_ami_id     = \"ami-xxxxxxxx\"\n  source_ami_region = \"us-east-1\"\n  encrypted         = false\n}\n\n# Fail\nresource \"aws_ami_copy\" \"encrypted_not_set\" {\n  name              = \"foo\"\n  source_ami_id     = \"ami-xxxxxxxx\"\n  source_ami_region = \"us-east-1\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/terraform12/encrypted.tf",
    "content": "## Setup Helper\nvariable \"test_ami\" {\n  default = \"ami-xxxxxxxx\"\n}\n\nvariable \"test_region\" {\n  default = \"us-east-1\"\n}\n\n# Pass\nresource \"aws_ami_copy\" \"encrypted_set_to_true\" {\n  name              = \"foo\"\n  source_ami_id     = var.test_ami\n  source_ami_region = var.test_region\n  encrypted         = true\n}\n\n# Fail\nresource \"aws_ami_copy\" \"encrypted_set_to_false\" {\n  name              = \"foo\"\n  source_ami_id     = var.test_ami\n  source_ami_region = var.test_region\n  encrypted         = false\n}\n\n# Fail\nresource \"aws_ami_copy\" \"encrypted_not_set\" {\n  name              = \"foo\"\n  source_ami_id     = var.test_ami\n  source_ami_region = var.test_region\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ami_copy/encrypted/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: AMI_COPY_SNAPSHOTS_ENCRYPTED\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EBS_VOLUME_ENCRYPTION\n    message: EBS Volume should be encrypted\n    resource: aws_ebs_volume\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n    tags:\n      - ec2\n      - ebs\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/terraform11/encrypted.tf",
    "content": "# Pass\nresource \"aws_ebs_volume\" \"encrypted_set_to_true\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n  encrypted         = true\n}\n\n# Fail\nresource \"aws_ebs_volume\" \"encrypted_set_to_false\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n  encrypted         = false\n}\n\n# Fail\nresource \"aws_ebs_volume\" \"encrypted_not_set\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/terraform12/encrypted.tf",
    "content": "# Pass\nresource \"aws_ebs_volume\" \"encrypted_set_to_true\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n  encrypted         = true\n}\n\n# Fail\nresource \"aws_ebs_volume\" \"encrypted_set_to_false\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n  encrypted         = false\n}\n\n# Fail\nresource \"aws_ebs_volume\" \"encrypted_not_set\" {\n  availability_zone = \"us-west-2a\"\n  size              = 20\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/ebs_volume/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: EBS_VOLUME_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EBS_BLOCK_DEVICE_ENCRYPTED\n    message: EBS block devices should use encryption\n    resource: aws_instance\n    severity: FAILURE\n    assertions:\n      - every:\n          key: ebs_block_device\n          expressions:\n            - key: encrypted\n              op: is-true\n    tags:\n      - ec2\n      - ebs\n\n  - id: EBS_BLOCK_DEVICE_ENCRYPTED_KMS\n    message: EBS block devices should specify a KMS key for encryption\n    resource: aws_instance\n    severity: WARNING\n    assertions:\n      - every:\n          key: ebs_block_device\n          expressions:\n            - key: kms_key_id\n              op: present\n    tags:\n      - ec2\n      - ebs"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/tests/terraform12/ebs_block_device_encrypted.tf",
    "content": "# Test that EBS block device is using encrpytion and specifies a KMS key\n# https://www.terraform.io/docs/providers/aws/r/instance.html#encrypted\n# https://www.terraform.io/docs/providers/aws/r/instance.html#kms_key_id\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n## Setup Helper\ndata \"aws_ami\" \"ubuntu\" {\n  most_recent = true\n\n  filter {\n    name   = \"name\"\n    values = [\"ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*\"]\n  }\n\n  filter {\n    name   = \"virtualization-type\"\n    values = [\"hvm\"]\n  }\n\n  owners = [\"099720109477\"] # Canonical\n}\n\n# PASS: Not specifiying an EBS block device\nresource \"aws_instance\" \"ebs_block_device_not_set\" {\n  ami           = data.aws_ami.ubuntu.id\n  instance_type = \"t2.micro\"\n}\n\n# PASS: Block device specified with encryption enabled and KMS key\nresource \"aws_instance\" \"ebs_block_device_encrypted_set_to_true\" {\n  ami           = data.aws_ami.ubuntu.id\n  instance_type = \"t2.micro\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 20\n    encrypted   = true\n    kms_key_id  = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890a\"\n  }\n}\n\n# FAIL: Encryption disabled\n# WARN: KMS key not specified\nresource \"aws_instance\" \"ebs_block_device_encrypted_set_to_false\" {\n  ami           = data.aws_ami.ubuntu.id\n  instance_type = \"t2.micro\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 20\n    encrypted   = false\n  }\n}\n\n# FAIL: Encryption not specified\n# WARN: KMS key not specified\nresource \"aws_instance\" \"ebs_block_device_encrypted_not_set\" {\n  ami           = data.aws_ami.ubuntu.id\n  instance_type = \"t2.micro\"\n\n  ebs_block_device {\n    device_name = \"/dev/xvda\"\n    volume_size = 20\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ec2/instance/ebs_block_device_encrypted/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: EBS_BLOCK_DEVICE_ENCRYPTED\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: EBS_BLOCK_DEVICE_ENCRYPTED_KMS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ECR_WILDCARD_PRINCIPAL\n    message: ECR allow policy should not use a wildcard princpal\n    resource: aws_ecr_repository_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - ecr\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "content": "# Test that ECR allow policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/ecr_repository_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Allow policy not using wildcard principal\nresource \"aws_ecr_repository_policy\" \"ecr_allow_no_wildcard\" {\n  repository = \"ecr-repo\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": \"arn:aws:iam::1234567890:user/foo\",\n            \"Action\": [\n                \"ecr:*\"\n            ],\n            \"Resource\": \"*\"\n        }\n    ]\n}\nEOF\n}\n\n\n# PASS: Deny policy using wildcard principal\nresource \"aws_ecr_repository_policy\" \"ecr_deny_wildcard\" {\n  repository = \"ecr-repo\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Deny\",\n            \"Principal\": \"arn:aws:iam::1234567890:user/*\",\n            \"Action\": [\n                \"ecr:*\"\n            ],\n            \"Resource\": \"*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL Allow policy using wildcard principal\nresource \"aws_ecr_repository_policy\" \"ecr_allow_with_wildcard\" {\n  repository = \"ecr-repo\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": \"arn:aws:iam::1234567890:user/*\",\n            \"Action\": [\n                \"ecr:*\"\n            ],\n            \"Resource\": \"*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow policy where principal is a wildcard\nresource \"aws_ecr_repository_policy\" \"ecr_allow_principal_is_wildcard\" {\n  repository = \"ecr-repo\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2008-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Principal\": \"*\",\n            \"Action\": [\n                \"ecr:*\"\n            ],\n            \"Resource\": \"*\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecr/ecr_repository_policy/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ECR_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ECS_ENVIRONMENT_SECRETS\n    message: Environment for ECS task definition should not include AWS secrets\n    resource: aws_ecs_task_definition\n    severity: FAILURE\n    # this rule fails if it finds a regex match for either the Access Key ID and/or the Secret Access Key\n    assertions:\n      - not:\n        - some:\n            key: container_definitions[].environment[]\n            expressions:\n              # Check if the string starts with any known 4 character ACCESS_KEY sequence\n              # and is 20 capital alpha-numeric characters long in total\n              - key: value\n                op: regex\n                value: \"^(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}$\"\n        - some:\n            key: container_definitions[].environment[]\n            expressions:\n              - and:\n                # Check if the string is exactly 40 characters long\n                - key: value\n                  op: regex\n                  value: \"^.{40}$\"\n                # Check if the string contains only alpha-numeric-slash-plus characters with at least 1 / or +\n                - key: value\n                  op: regex\n                  value: \"^[a-zA-Z0-9/+]+[/+]+[a-zA-Z0-9/+]+$\"\n    tags:\n      - ecs\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/terraform11/secrets.tf",
    "content": "# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_not_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"bar\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_20_character_capital_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AXYZIOSFODNN7EXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_21_character_capital_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AKIAIOSFODNN7FEXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_40_character_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFEMI>K7MDENG^bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_41_character_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFOOMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AKIAIOSFODNN7EXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secret_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_access_key_and_secret_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AIPAIOSFODNN7EXAMPLE\"\n        },\n        {\n            \"name\": \"bar\",\n            \"value\": \"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/terraform12/secrets.tf",
    "content": "# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_not_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"bar\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_20_character_capital_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AXYZIOSFODNN7EXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_21_character_capital_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AKIAIOSFODNN7FEXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_40_character_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFEMI>K7MDENG^bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Pass\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secrets_not_set_41_character_string\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFOOMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AKIAIOSFODNN7EXAMPLE\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_secret_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n\n# Fail\nresource \"aws_ecs_task_definition\" \"container_definitions_environment_aws_access_key_and_secret_access_key_set\" {\n  family                = \"foo\"\n  container_definitions = <<EOF\n[\n  {\n    \"name\": \"bar\",\n    \"image\": \"foobar\",\n    \"cpu\": 10,\n    \"memory\": 512,\n    \"essential\": true,\n    \"portMappings\": [\n      {\n        \"containerPort\": 80,\n        \"hostPort\": 80\n      }\n    ],\n    \"environment\": [\n        {\n            \"name\": \"foo\",\n            \"value\": \"AIPAIOSFODNN7EXAMPLE\"\n        },\n        {\n            \"name\": \"bar\",\n            \"value\": \"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY\"\n        }\n    ]\n  }\n]\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ecs/ecs_task_definition/task_definition_secrets/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ECS_ENVIRONMENT_SECRETS\n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EFS_ENCRYPTED\n    message: EFS should be encrypted\n    resource: aws_efs_file_system\n    severity: FAILURE\n    assertions:\n      - and:\n        - key: encrypted\n          op: is-true\n    tags:\n      - efs\n"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/terraform11/encrypted.tf",
    "content": "# Pass\nresource \"aws_efs_file_system\" \"encrypted_set_to_true\" {\n  creation_token = \"foo\"\n  encrypted      = true\n}\n\n# Fail\nresource \"aws_efs_file_system\" \"encrypted_set_to_false\" {\n  creation_token = \"foo\"\n  encrypted      = false\n}\n\n# Fail\nresource \"aws_efs_file_system\" \"encrypted_not_set\" {\n  creation_token = \"foo\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/terraform12/encrypted.tf",
    "content": "# Pass\nresource \"aws_efs_file_system\" \"encrypted_set_to_true\" {\n  creation_token = \"foo\"\n  encrypted      = true\n}\n\n# Fail\nresource \"aws_efs_file_system\" \"encrypted_set_to_false\" {\n  creation_token = \"foo\"\n  encrypted      = false\n}\n\n# Fail\nresource \"aws_efs_file_system\" \"encrypted_not_set\" {\n  creation_token = \"foo\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/efs/efs_file_system/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: EFS_ENCRYPTED\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n  - id: ALB_ACCESS_LOGS\n    message: ALB should enable access logs\n    resources:\n      - aws_alb\n    severity: FAILURE\n    assertions:\n      - key: access_logs\n        op: present\n      - every:\n          key: access_logs\n          expressions:\n            - key: enabled\n              op: is-true\n    tags:\n      - alb\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.test_key.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_alb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_enabled_set_to_false\" {\n  access_logs {\n    bucket  = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix  = \"foo\"\n    enabled = false\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_enabled_not_set\" {\n  access_logs {\n    bucket = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix = \"foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = aws_kms_key.test_key.arn\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_alb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = aws_s3_bucket.test_bucket.bucket\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_enabled_set_to_false\" {\n  access_logs {\n    bucket  = aws_s3_bucket.test_bucket.bucket\n    prefix  = \"foo\"\n    enabled = false\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_enabled_not_set\" {\n  access_logs {\n    bucket = aws_s3_bucket.test_bucket.bucket\n    prefix = \"foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb\" \"access_logs_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb/alb_access_logs_enabled/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ALB_ACCESS_LOGS\n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENER_HTTPS\n    message: ALB listener should use HTTPS\n    resources:\n      - aws_alb_listener\n      - aws_lb_listener\n    severity: FAILURE\n    assertions:\n      - key: port\n        op: eq\n        value: 443\n      - key: protocol\n        op: regex\n        value: \"(?i)HTTPS\"\n      - key: ssl_policy\n        op: present\n      - key: certificate_arn\n        op: present\n    tags:\n      - alb\n      - lb\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/terraform11/https.tf",
    "content": "# Pass\nresource \"aws_alb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_alb_listener\" \"listener_secure_https_set_lowercase\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"https\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"port_set_to_80\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"80\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"protocol_set_to_http\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTP\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"ssl_policy_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"certificate_arn_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/terraform12/https.tf",
    "content": "## Setup Helper\nresource \"aws_vpc\" \"test_vpc\" {\n  cidr_block = \"10.0.0.0/16\"\n}\n\nresource \"aws_acm_certificate\" \"test_cert\" {\n  domain_name       = \"foobar.com\"\n  validation_method = \"DNS\"\n\n  lifecycle {\n    create_before_destroy = true\n  }\n}\n\nresource \"aws_lb\" \"test_lb\" {\n}\n\nresource \"aws_lb_target_group\" \"test_lb_target_group\" {\n  vpc_id = aws_vpc.test_vpc.id\n}\n\n# Pass\nresource \"aws_alb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = aws_acm_certificate.test_cert.arn\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n\n# Pass\nresource \"aws_alb_listener\" \"listener_secure_https_set_lowercase\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"443\"\n  protocol          = \"https\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = aws_acm_certificate.test_cert.arn\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"port_set_to_80\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"80\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = aws_acm_certificate.test_cert.arn\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"protocol_set_to_http\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"443\"\n  protocol          = \"HTTP\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = aws_acm_certificate.test_cert.arn\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"ssl_policy_not_set\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  certificate_arn   = aws_acm_certificate.test_cert.arn\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n\n# Fail\nresource \"aws_alb_listener\" \"certificate_arn_not_set\" {\n  load_balancer_arn = aws_lb.test_lb.arn\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = aws_lb_target_group.test_lb_target_group.arn\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/alb_listener/alb_listener_https/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ALB_LISTENER_HTTPS\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELB_ACCESS_LOGGING\n    message: ELB should enable access logging\n    resource: aws_elb\n    severity: WARNING\n    assertions:\n      # Check if 'access_logs' is present and if it is then we can check to see if the \n      # key 'enabled' exists within the 'access_logs' array.\n      # If it does NOT exist, then the value is True by default.\n      # If it does exist, then the value should be set to True.\n      - key: access_logs\n        op: present\n      - or:\n        - every:\n            key: access_logs[]\n            expressions:\n              - key: enabled\n                op: absent\n        - every:\n            key: access_logs[]\n            expressions:\n              - key: enabled\n                op: is-true\n    tags:\n      - elb\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "content": "# Pass\nresource \"aws_elb\" \"access_logs_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Pass\nresource \"aws_elb\" \"access_logs_enabled\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n    enabled       = true\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Warn\nresource \"aws_elb\" \"access_logs_not_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Warn\nresource \"aws_elb\" \"access_logs_disabled\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n    enabled       = false\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "content": "# Pass\nresource \"aws_elb\" \"access_logs_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Pass\nresource \"aws_elb\" \"access_logs_enabled\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n    enabled       = true\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Warn\nresource \"aws_elb\" \"access_logs_not_set\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n\n# Warn\nresource \"aws_elb\" \"access_logs_disabled\" {\n  availability_zones = [\n    \"us-east-1a\",\n    \"us-east-1b\",\n    \"us-east-1c\"\n  ]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n    enabled       = false\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/elb/access_logs_enabled/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELB_ACCESS_LOGGING\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LB_ACCESS_LOGS\n    message: LB should enable access logs\n    resources:\n      - aws_lb\n    severity: FAILURE\n    assertions:\n      - key: access_logs\n        op: present\n      - every:\n          key: access_logs\n          expressions:\n            - key: enabled\n              op: is-true\n    tags:\n      - lb\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/terraform11/access_logs_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.test_key.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_lb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_enabled_set_to_false\" {\n  access_logs {\n    bucket  = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix  = \"foo\"\n    enabled = false\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_enabled_not_set\" {\n  access_logs {\n    bucket = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix = \"foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/terraform12/access_logs_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = aws_kms_key.test_key.arn\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_lb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = aws_s3_bucket.test_bucket.bucket\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_enabled_set_to_false\" {\n  access_logs {\n    bucket  = aws_s3_bucket.test_bucket.bucket\n    prefix  = \"foo\"\n    enabled = false\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_enabled_not_set\" {\n  access_logs {\n    bucket = aws_s3_bucket.test_bucket.bucket\n    prefix = \"foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb\" \"access_logs_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb/access_logs_enabled/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: LB_ACCESS_LOGS\n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENER_HTTPS\n    message: ALB listener should use HTTPS\n    resources:\n      - aws_alb_listener\n      - aws_lb_listener\n    severity: FAILURE\n    assertions:\n      - key: port\n        op: eq\n        value: 443\n      - key: protocol\n        op: regex\n        value: \"(?i)HTTPS\"\n      - key: ssl_policy\n        op: present\n      - key: certificate_arn\n        op: present\n    tags:\n      - alb\n      - lb\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/terraform11/https.tf",
    "content": "# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set_lowercase\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"https\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"port_set_to_80\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"80\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"protocol_set_to_http\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTP\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"certificate_arn_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/terraform12/https.tf",
    "content": "# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"listener_secure_https_set_lowercase\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"https\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"port_set_to_80\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"80\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"protocol_set_to_http\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTP\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"certificate_arn_not_set\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_https/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ALB_LISTENER_HTTPS\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ALB_LISTENER_SSL_POLICY\n    message: ALB listener shouldn't use outdated Predefined Security Policies\n    resources:\n      - aws_alb_listener\n      - aws_lb_listener\n    severity: FAILURE\n    assertions:\n      - key: ssl_policy\n        op: not-in\n        value: ELBSecurityPolicy-2015-05,ELBSecurityPolicy-2015-03,ELBSecurityPolicy-2015-02\n      - key: ssl_policy\n        op: not-in\n        value: ELBSecurityPolicy-2014-10,ELBSecurityPolicy-2014-01\n      - key: ssl_policy\n        op: not-in\n        value: ELBSecurityPolicy-2011-08\n    tags:\n      - alb\n      - lb\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/terraform11/ssl_policy.tf",
    "content": "# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2016-08\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-TLS-1-2-2017-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-TLS-1-2-2017-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-TLS-1-1-2017-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-TLS-1-1-2017-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-05\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-05\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-03\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-03\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-02\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-02\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2014-10\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2014-10\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2014-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2014-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2011-08\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2011-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/terraform12/ssl_policy.tf",
    "content": "# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2016-08\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2016-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-TLS-1-2-2017-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-TLS-1-2-2017-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Pass\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-TLS-1-1-2017-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-TLS-1-1-2017-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-05\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-05\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-03\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-03\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2015-02\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2015-02\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2014-10\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2014-10\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2014-01\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2014-01\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n\n# Fail\nresource \"aws_lb_listener\" \"ssl_policy_set_to_ELBSecurityPolicy-2011-08\" {\n  load_balancer_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:loadbalancer/app/foo\"\n  port              = \"443\"\n  protocol          = \"HTTPS\"\n  ssl_policy        = \"ELBSecurityPolicy-2011-08\"\n  certificate_arn   = \"arn:aws:iam::1234567890:server-certificate/foo\"\n\n  default_action {\n    type             = \"forward\"\n    target_group_arn = \"arn:aws:elasticloadbalancing:us-east-1:1234567890:targetgroup/foo\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastic_load_balancing/lb_listener/listener_ssl_policy/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ALB_LISTENER_SSL_POLICY\n    warnings: 0\n    failures: 6\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICACHE_ENCRYPTION_REST\n    message: ElastiCache ReplicationGroup should have encryption at rest enabled\n    resource: aws_elasticache_replication_group\n    severity: FAILURE\n    assertions:\n    - key: at_rest_encryption_enabled\n      op: is-true\n    tags:\n      - elasticache\n\n  - id: ELASTICACHE_ENCRYPTION_REST_KMS\n    message: ElastiCache ReplicationGroup should have a KMS key specified\n    resource: aws_elasticache_replication_group\n    severity: WARNING\n    assertions:\n    - key: kms_key_id\n      op: present\n    tags:\n      - elasticache"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/tests/terraform12/encryption_at_rest.tf",
    "content": "# Test that at-rest encryption is enabled with a KMS key\n# https://www.terraform.io/docs/providers/aws/r/elasticache_replication_group.html#at_rest_encryption_enabled\n# https://www.terraform.io/docs/providers/aws/r/elasticache_replication_group.html#kms_key_id\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Encryption at rest is enabled with a KMS key\nresource \"aws_elasticache_replication_group\" \"at_rest_encryption_enabled_is_set_to_true\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  at_rest_encryption_enabled    = true\n  kms_key_id                    = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# FAIL: Encryption at rest is disabled\n# WARN: KMS key is not provided\nresource \"aws_elasticache_replication_group\" \"at_rest_encryption_enabled_is_set_to_false\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  at_rest_encryption_enabled    = false\n}\n\n# FAIL: Encryption at rest is not specified\n# WARN: KMS key is not provided\nresource \"aws_elasticache_replication_group\" \"at_rest_encryption_enabled_is_not_set\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_at_rest/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICACHE_ENCRYPTION_REST\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: ELASTICACHE_ENCRYPTION_REST_KMS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICACHE_ENCRYPTION_TRANSIT\n    message: ElastiCache ReplicationGroup should have encryption in transit enabled\n    resource: aws_elasticache_replication_group\n    severity: FAILURE\n    assertions:\n    - key: transit_encryption_enabled\n      op: is-true\n    tags:\n      - elasticache\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/terraform11/encryption_in_transit.tf",
    "content": "# Pass\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_true\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  transit_encryption_enabled    = true\n}\n\n# Fail\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_false\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  transit_encryption_enabled    = false\n}\n\n# Fail\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_not_set\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/terraform12/encryption_in_transit.tf",
    "content": "# Pass\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_true\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  transit_encryption_enabled    = true\n}\n\n# Fail\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_set_to_false\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  transit_encryption_enabled    = false\n}\n\n# Fail\nresource \"aws_elasticache_replication_group\" \"transit_encryption_enabled_is_not_set\" {\n  replication_group_id          = \"foo\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticache/elasticache_replication_group/encryption_in_transit/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICACHE_ENCRYPTION_TRANSIT\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEARCH_ENCRYPTION_REST\n    message: Elasticsearch domain must be encrypted at rest\n    resource: aws_elasticsearch_domain\n    severity: FAILURE\n    assertions:\n      - key: encrypt_at_rest\n        op: present\n      - every:\n          key: encrypt_at_rest\n          expressions:\n            - key: enabled\n              op: is-true\n    tags:\n      - elasticsearch"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/tests/terraform12/encryption_at_rest.tf",
    "content": "# Test for encryption at rest options on an elasticsearch domain\n# https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain.html#encrypt_at_rest\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# FAIL: encrypt_at_rest not provided\nresource \"aws_elasticsearch_domain\" \"es_domain_encryption_at_rest_not_provided\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n}\n\n# FAIL: encrypt_at_rest provided but disabled\nresource \"aws_elasticsearch_domain\" \"es_domain_encryption_at_rest_not_enabled\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n\n  encrypt_at_rest {\n    enabled = false\n  }\n}\n\n# PASS: encrypt_at_rest provided and enabled\nresource \"aws_elasticsearch_domain\" \"es_domain_encryption_at_rest_enabled\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n\n  encrypt_at_rest {\n    enabled = true\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_at_rest/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICSEARCH_ENCRYPTION_REST\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEARCH_NODE_TO_NODE_ENCRYPTION\n    message: Elasticsearch domain must have encryption at rest\n    resource: aws_elasticsearch_domain\n    severity: FAILURE\n    assertions:\n      - key: node_to_node_encryption\n        op: present\n      - every:\n          key: node_to_node_encryption\n          expressions:\n            - key: enabled\n              op: is-true\n    tags:\n      - elasticsearch"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/tests/terraform12/encryption_node_to_node.tf",
    "content": "# Test for node to node encryption for an elasticsearch domain\n# https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain.html#node_to_node_encryption\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: node_to_node_encryption provided and enabled\nresource \"aws_elasticsearch_domain\" \"es_domain_node_to_node_encryption_enabled\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n\n  node_to_node_encryption {\n    enabled = true\n  }\n}\n\n# FAIL: node_to_node_encryption option not provided\nresource \"aws_elasticsearch_domain\" \"es_domain_node_to_node_encryption_not_provided\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n}\n\n# FAIL: node_to_node_encryption provided but disabled\nresource \"aws_elasticsearch_domain\" \"es_domain_node_to_node_encryption_disabled\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n\n  node_to_node_encryption {\n    enabled = false\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/encryption_node_to_node/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICSEARCH_NODE_TO_NODE_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEARCH_VPC\n    message: Elasticsearch domain must be in a VPC\n    resource: aws_elasticsearch_domain\n    severity: FAILURE\n    assertions:\n      - key: vpc_options\n        op: present\n    tags:\n      - elasticsearch"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/tests/terraform12/elasticsearch_vpc.tf",
    "content": "# Test that elasticsearch domain is in a VPC using vpc_options\n# https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain.html#vpc_options\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# FAIL: vpc_options is not provided\nresource \"aws_elasticsearch_domain\" \"es_domain_vpc_options_not_provided\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n}\n\n# PASS: vpc_options is provided\nresource \"aws_elasticsearch_domain\" \"es_domain_vpc_options_provided\" {\n  domain_name           = \"example\"\n  elasticsearch_version = \"1.5\"\n\n  cluster_config {\n    instance_type = \"r4.large.elasticsearch\"\n  }\n\n  vpc_options {\n    subnet_ids = [\n      \"Subnet1\",\n      \"Subnet2\"\n    ]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/elasticsearch_domain/vpc_subnets/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICSEARCH_VPC\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ELASTICSEARCH_POLICY_WILDCARD_PRINCIPAL\n    message: Elasticsearch allow policy should not use a wildcard princpal\n    resources:\n      - aws_elasticsearch_domain_policy\n      - aws_elasticsearch_domain\n    severity: FAILURE\n    assertions:\n      - none:\n          key: access_policies.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - elasticsearch\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/terraform12/elasticsearch_domain_policy_wildcard_principal.tf",
    "content": "# Test that an elasticsearch domain policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain_policy.html\n\n# Helper\nresource \"aws_elasticsearch_domain\" \"example\" {\n  domain_name           = \"tf-test\"\n  elasticsearch_version = \"2.3\"\n}\n\n# PASS: Allow principal does not contain a wildcard\nresource \"aws_elasticsearch_domain_policy\" \"policy_allow_principal_no_wildcard\" {\n  domain_name = aws_elasticsearch_domain.example.domain_name\n\n  access_policies = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny principal doesn't contain a wildcard\nresource \"aws_elasticsearch_domain_policy\" \"policy_deny_principal_no_wildcard\" {\n  domain_name = aws_elasticsearch_domain.example.domain_name\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# PASS: Deny principal contains a wildcard\nresource \"aws_elasticsearch_domain_policy\" \"policy_deny_principal_contains_wildcard\" {\n  domain_name = aws_elasticsearch_domain.example.domain_name\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# FAIL: Allow principal contains a wildcard\nresource \"aws_elasticsearch_domain_policy\" \"policy_allow_principal_contains_wildcard\" {\n  domain_name = aws_elasticsearch_domain.example.domain_name\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# FAIL: Principal is a wildcard\nresource \"aws_elasticsearch_domain_policy\" \"policy_allow_principal_is_wildcard\" {\n  domain_name = aws_elasticsearch_domain.example.domain_name\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": \"*\",\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/terraform12/elasticsearch_domain_wildcard_principal.tf",
    "content": "# Test that an elasticsearch domain policy is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/elasticsearch_domain.html#access_policies\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Allow principal does not contain a wildcard\nresource \"aws_elasticsearch_domain\" \"allow_principal_no_wildcard\" {\n  domain_name = \"tf-test\"\n\n  access_policies = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny principal doesn't contain a wildcard\nresource \"aws_elasticsearch_domain\" \"deny_principal_no_wildcard\" {\n  domain_name = \"tf-test\"\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# PASS: Deny principal contains a wildcard\nresource \"aws_elasticsearch_domain\" \"deny_principal_contains_wildcard\" {\n  domain_name = \"tf-test\"\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# FAIL: Allow principal contains a wildcard\nresource \"aws_elasticsearch_domain\" \"allow_principal_contains_wildcard\" {\n  domain_name = \"tf-test\"\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n\n# FAIL: Principal is a wildcard\nresource \"aws_elasticsearch_domain\" \"allow_principal_is_wildcard\" {\n  domain_name = \"tf-test\"\n\n  access_policies = <<POLICIES\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"es:ListDomainNames\",\n            \"Principal\": \"*\",\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:es:us-east-1:123456789012:domain/test/*\"\n        }\n    ]\n}\nPOLICIES\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elasticsearch/shared/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ELASTICSEARCH_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: TRANSCODER_REQUIRE_ENCRYPTION\n    message: Elastictranscoder pipeline requires encryption\n    resource: aws_elastictranscoder_pipeline\n    severity: FAILURE\n    assertions:\n      - key: aws_kms_key_arn\n        op: present\n    tags:\n      - elastictranscoder"
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/tests/terraform12/require_encryption.tf",
    "content": "# Test that encryption is enabled\n# https://www.terraform.io/docs/providers/aws/r/elastictranscoder_pipeline.html#aws_kms_key_arn\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: KMS key is defined\nresource \"aws_elastictranscoder_pipeline\" \"transcoder_kms_key_defined\" {\n  input_bucket = \"MyBucket\"\n  name         = \"aws_elastictranscoder_pipeline_tf_test_\"\n  role         = \"arn:aws:iam::123456789012:role/example-role\"\n\n  aws_kms_key_arn = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n\n  content_config {\n    bucket        = \"MyBucket\"\n    storage_class = \"Standard\"\n  }\n\n  thumbnail_config {\n    bucket        = \"MyBucket\"\n    storage_class = \"Standard\"\n  }\n}\n\n# FAIL: KMS key is not defined\nresource \"aws_elastictranscoder_pipeline\" \"transcoder_kms_key_not_defined\" {\n  input_bucket = \"MyBucket\"\n  name         = \"aws_elastictranscoder_pipeline_tf_test_\"\n  role         = \"arn:aws:iam::123456789012:role/example-role\"\n\n  content_config {\n    bucket        = \"MyBucket\"\n    storage_class = \"Standard\"\n  }\n\n  thumbnail_config {\n    bucket        = \"MyBucket\"\n    storage_class = \"Standard\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/elastictranscoder/elastictranscoder_pipeline/require_encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: TRANSCODER_REQUIRE_ENCRYPTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AWS_EMR_CLUSTER_LOGGING\n    message: AWS EMR Should have logging enabled\n    resource: aws_emr_cluster\n    severity: WARNING\n    assertions:\n      - key: log_uri\n        op: present\n    tags:\n      - emr\n"
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/terraform11/logging.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name          = \"foo\"\n  release_label = \"emr-4.6.0\"\n  service_role  = \"arn:aws:iam::1234567890:role/EMR_DefaultRole\"\n  log_uri       = \"s3://${aws_s3_bucket.test_bucket.bucket}/\"\n}\n\n# Fail\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name          = \"foo\"\n  release_label = \"emr-4.6.0\"\n  service_role  = \"arn:aws:iam::1234567890:role/EMR_DefaultRole\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/terraform12/logging.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name          = \"foo\"\n  release_label = \"emr-4.6.0\"\n  service_role  = \"arn:aws:iam::1234567890:role/EMR_DefaultRole\"\n  log_uri       = \"s3://${aws_s3_bucket.test_bucket.bucket}/\"\n}\n\n# Fail\nresource \"aws_emr_cluster\" \"log_uri_is_set\" {\n  name          = \"foo\"\n  release_label = \"emr-4.6.0\"\n  service_role  = \"arn:aws:iam::1234567890:role/EMR_DefaultRole\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/emr/emr_cluster/logging/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: AWS_EMR_CLUSTER_LOGGING\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: GLUE_CONNECTION_PROPERTIES\n    message: Glue connection properties cannot specify plaintext passwords\n    resource: aws_glue_connection\n    severity: FAILURE\n    assertions:\n      - key: connection_properties[].PASSWORD | [0]\n        op: absent\n    tags:\n      - glue"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/tests/terraform12/connection_properties.tf",
    "content": "# Test that connection_properties is not providing a plaintext password\n# https://www.terraform.io/docs/providers/aws/r/glue_connection.html#connection_properties\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: connection_properties not used\nresource \"aws_glue_connection\" \"glue_connection_properties_password_not_used\" {\n  connection_properties = {\n    JDBC_CONNECTION_URL = \"jdbc:mysql://example.com/exampledatabase\"\n    USERNAME            = \"exampleusername\"\n  }\n  name = \"example\"\n}\n\n# FAIL: connection_properties are being used\nresource \"aws_glue_connection\" \"glue_connection_properties_password_used\" {\n  connection_properties = {\n    JDBC_CONNECTION_URL = \"jdbc:mysql://example.com/exampledatabase\"\n    PASSWORD            = \"examplepassword\"\n    USERNAME            = \"exampleusername\"\n  }\n  name = \"example\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/glue/glue_connection/connection_properties/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: GLUE_CONNECTION_PROPERTIES\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_USER_GROUP\n    message: IAM User should be in at least one group\n    resource: aws_iam_group_membership\n    severity: FAILURE\n    assertions:\n      - key: group\n        op: not-empty\n      - key: users\n        op: not-empty\n    tags:\n      - iam\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/terraform11/group_and_users.tf",
    "content": "## Setup Helper\nresource \"aws_iam_group\" \"test_group\" {\n  name = \"test-group\"\n}\n\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"test-user\"\n}\n\n# Pass\nresource \"aws_iam_group_membership\" \"group_and_users_set\" {\n  name = \"tf-testing-group-membership\"\n\n  group = \"${aws_iam_group.test_group.name}\"\n\n  users = [\n    \"${aws_iam_user.test_user.name}\"\n  ]\n}\n\n# Fail\nresource \"aws_iam_group_membership\" \"group_set_and_users_empty\" {\n  name = \"tf-testing-group-membership\"\n\n  users = []\n\n  group = \"${aws_iam_group.test_group.name}\"\n}\n\n# Fail\nresource \"aws_iam_group_membership\" \"group_empty_and_users_set\" {\n  name = \"tf-testing-group-membership\"\n\n  users = [\n    \"${aws_iam_user.test_user.name}\"\n  ]\n\n  group = \"\"\n}\n\n# Fail x 2\nresource \"aws_iam_group_membership\" \"group_empty_and_users_empty\" {\n  name = \"tf-testing-group-membership\"\n\n  users = []\n\n  group = \"\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/terraform12/group_and_users.tf",
    "content": "## Setup Helper\nresource \"aws_iam_group\" \"test_group\" {\n  name = \"test-group\"\n}\n\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"test-user\"\n}\n\n# Pass\nresource \"aws_iam_group_membership\" \"group_and_users_set\" {\n  name = \"tf-testing-group-membership\"\n\n  group = aws_iam_group.test_group.name\n\n  users = [\n    aws_iam_user.test_user.name\n  ]\n}\n\n# Fail\nresource \"aws_iam_group_membership\" \"group_set_and_users_empty\" {\n  name = \"tf-testing-group-membership\"\n\n  users = []\n\n  group = aws_iam_group.test_group.name\n}\n\n# Fail\nresource \"aws_iam_group_membership\" \"group_empty_and_users_set\" {\n  name = \"tf-testing-group-membership\"\n\n  users = [\n    aws_iam_user.test_user.name\n  ]\n\n  group = \"\"\n}\n\n# Fail x 2\nresource \"aws_iam_group_membership\" \"group_empty_and_users_empty\" {\n  name = \"tf-testing-group-membership\"\n\n  users = []\n\n  group = \"\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_group_membership/group_and_users/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_USER_GROUP\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard action in an Allow IAM policy\n    resource: aws_iam_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Action\n              op: contains\n              value: \"*\"\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/terraform11/policy_action_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/terraform12/policy_action_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_action_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_POLICY_WILDCARD_ACTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_NOT_ACTION\n    message: Should not use NotAction in IAM policy\n    resource: aws_iam_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/terraform11/policy_notaction.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_policy\" \"policy_statement_with_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyAllUsersNotUsingMFA\",\n      \"Effect\": \"Deny\",\n      \"NotAction\": \"iam:*\",\n      \"Resource\": \"*\",\n      \"Condition\": {\"BoolIfExists\": {\"aws:MultiFactorAuthPresent\": \"false\"}}\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/terraform12/policy_notaction.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_policy\" \"policy_statement_with_notaction\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyAllUsersNotUsingMFA\",\n      \"Effect\": \"Deny\",\n      \"NotAction\": \"iam:*\",\n      \"Resource\": \"*\",\n      \"Condition\": {\"BoolIfExists\": {\"aws:MultiFactorAuthPresent\": \"false\"}}\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_POLICY_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_NOT_RESOURCE\n    message: Should not use NotResource in IAM policy\n    resource: aws_iam_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotResource\n              op: present\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/terraform11/policy_notresource.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_policy\" \"policy_statement_with_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"NotResource\": [\n        \"arn:aws:s3:::HRBucket/Payroll\",\n        \"arn:aws:s3:::HRBucket/Payroll/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/terraform12/policy_notresource.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_without_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_policy\" \"policy_statement_with_notresource\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"NotResource\": [\n        \"arn:aws:s3:::HRBucket/Payroll\",\n        \"arn:aws:s3:::HRBucket/Payroll/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_notresource/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_POLICY_NOT_RESOURCE\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_POLICY_WILDCARD_RESOURCE\n    message: Should not use wildcard resource in an Allow IAM policy\n    resource: aws_iam_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Resource\n              op: contains\n              value: \"*\"\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/terraform11/policy_resource_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_policy\" \"policy_statement_deny_resource_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/terraform12/policy_resource_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_policy\" \"policy_statement_allow_resource_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_policy\" \"policy_statement_deny_resource_with_wildcard\" {\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_policy/policy_resource_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_POLICY_WILDCARD_RESOURCE\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_action_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_WILDCARD_ACTION\n    message: Should not use wildcard action in role assume policy document\n    resource: aws_iam_role\n    severity: FAILURE\n    assertions:\n      - none:\n          key: assume_role_policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Action\n              op: contains\n              value: \"*\"\n    tags:\n      - iam\n      - role\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_action_wildcard/tests/terraform11/assume_role_policy_action_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_allow_action_without_wildcard\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role\" \"assume_role_policy_statement_allow_action_with_wildcard\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:*\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_action_wildcard/tests/terraform12/assume_role_policy_action_wildcard.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_allow_action_without_wildcard\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role\" \"assume_role_policy_statement_allow_action_with_wildcard\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:*\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_action_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_WILDCARD_ACTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_NOT_ACTION\n    message: Should not use NotAction in assume policy document\n    resource: aws_iam_role\n    severity: WARNING\n    assertions:\n      - none:\n          key: assume_role_policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - iam\n      - role\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notaction/tests/terraform11/assume_role_policy_notaction.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_without_NotAction\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"\",\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      }\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role\" \"assume_role_policy_statement_with_NotAction\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyAllUsersNotUsingMFA\",\n      \"Effect\": \"Deny\",\n      \"NotAction\": \"iam:*\",\n      \"Resource\": \"*\",\n      \"Condition\": {\"BoolIfExists\": {\"aws:MultiFactorAuthPresent\": \"false\"}}\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notaction/tests/terraform12/assume_role_policy_notaction.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_without_NotAction\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"\",\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      }\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role\" \"assume_role_policy_statement_with_NotAction\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"DenyAllUsersNotUsingMFA\",\n      \"Effect\": \"Deny\",\n      \"NotAction\": \"iam:*\",\n      \"Resource\": \"*\",\n      \"Condition\": {\"BoolIfExists\": {\"aws:MultiFactorAuthPresent\": \"false\"}}\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notprincipal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in role assume policy document\n    resource: aws_iam_role\n    severity: WARNING\n    assertions:\n      - none:\n          key: assume_role_policy.Statement[]\n          expressions:\n            - key: NotPrincipal\n              op: present\n    tags:\n      - iam\n      - role\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notprincipal/tests/terraform11/assume_role_policy_notprincipal.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_without_NotPrincipal\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"\",\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      }\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role\" \"assume_role_policy_statement_with_NotPrincipal\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Action\": \"s3:*\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket\",\n        \"arn:aws:s3:::fooBucket/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notprincipal/tests/terraform12/assume_role_policy_notprincipal.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_statement_without_NotPrincipal\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"\",\n      \"Effect\": \"Allow\",\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      }\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role\" \"assume_role_policy_statement_with_NotPrincipal\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Action\": \"s3:*\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket\",\n        \"arn:aws:s3:::fooBucket/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_notprincipal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_NOT_PRINCIPAL\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_version/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: ASSUME_ROLEPOLICY_VERSION\n    message: Version in IAM Policy should be 2012-10-17\n    resource: aws_iam_role\n    severity: FAILURE\n    assertions:\n      - key: assume_role_policy.Version\n        op: eq\n        value: \"2012-10-17\"\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_version/tests/terraform11/assume_role_policy_version.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_version_set_correctly\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role\" \"assume_role_policy_version_set_incorrectly\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_version/tests/terraform12/assume_role_policy_version.tf",
    "content": "# Pass\nresource \"aws_iam_role\" \"assume_role_policy_version_set_correctly\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role\" \"assume_role_policy_version_set_incorrectly\" {\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"rds:DeleteDBSnapshot\",\n      \"Resource\": \"arn:aws:rds:*:*:snapshot:*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role/assume_role_policy_version/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: ASSUME_ROLEPOLICY_VERSION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_action_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard action in an Allow IAM policy\n    resource: aws_iam_role_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Action\n              op: contains\n              value: \"*\"\n    tags:\n      - iam\n      - role\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_action_wildcard/tests/terraform11/policy_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_action_wildcard/tests/terraform12/policy_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_action_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_POLICY_WILDCARD_ACTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_POLICY_NOT_ACTION\n    message: Should not use NotAction in IAM policy\n    resource: aws_iam_role_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - iam\n      - role\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notaction/tests/terraform11/policy_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_without_notaction\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role_policy\" \"policy_statement_with_notaction\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"NotAction\": \"ec2:DescribeVpcs\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notaction/tests/terraform12/policy_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_without_notaction\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role_policy\" \"policy_statement_with_notaction\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"NotAction\": \"ec2:DescribeVpcs\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_POLICY_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notresource/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_POLICY_NOT_RESOURCE\n    message: Should not use NotResource in IAM policy\n    resource: aws_iam_role_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotResource\n              op: present\n    tags:\n      - iam\n      - role\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notresource/tests/terraform11/policy_notresource.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_without_notresource\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role_policy\" \"policy_statement_with_notresource\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"NotResource\": [\n        \"arn:aws:s3:::HRBucket/Payroll\",\n        \"arn:aws:s3:::HRBucket/Payroll/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notresource/tests/terraform12/policy_notresource.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_without_notresource\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_iam_role_policy\" \"policy_statement_with_notresource\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"NotResource\": [\n        \"arn:aws:s3:::HRBucket/Payroll\",\n        \"arn:aws:s3:::HRBucket/Payroll/*\"\n      ]\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_notresource/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_POLICY_NOT_RESOURCE\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_resource_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_ROLE_POLICY_WILDCARD_RESOURCE\n    message: Should not use wildcard resource in an Allow IAM policy\n    resource: aws_iam_role_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Resource\n              op: contains\n              value: \"*\"\n    tags:\n      - iam\n      - role\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_resource_wildcard/tests/terraform11/policy_resource_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role_policy\" \"policy_statement_allow_resource_with_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_deny_resource_with_wildcard\" {\n  role = \"${aws_iam_role.test_role.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_resource_wildcard/tests/terraform12/policy_resource_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = <<-EOF\n  {\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n      {\n        \"Action\": \"sts:AssumeRole\",\n        \"Principal\": {\n          \"Service\": \"ec2.amazonaws.com\"\n        },\n        \"Effect\": \"Allow\",\n        \"Sid\": \"\"\n      }\n    ]\n  }\n  EOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_allow_resource_without_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_role_policy\" \"policy_statement_allow_resource_with_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n\n# Pass\nresource \"aws_iam_role_policy\" \"policy_statement_deny_resource_with_wildcard\" {\n  role = aws_iam_role.test_role.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:Add*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"arn:aws:rds:*:*:cluster:*\"\n    }\n  ]\n}\n\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_role_policy/role_policy_resource_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_ROLE_POLICY_WILDCARD_RESOURCE\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy/exists/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_USER_POLICY\n    message: IAM User Policy should not be used (make user a member of group instead)\n    resource: aws_iam_user_policy\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n    tags:\n      - iam\n  - id: IAM_USER_POLICY_ATTACHMENT\n    message: IAM user should not have policies attached (make user a member of a group instead)\n    resource: aws_iam_user_policy_attachment\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n    tags:\n      - iam\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy/exists/tests/terraform11/resource_exists.tf",
    "content": "## Setup Helper\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"foobar\"\n}\n\n# Fail\nresource \"aws_iam_user_policy\" \"resource_exists\" {\n  user = \"${aws_iam_user.test_user.name}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy/exists/tests/terraform12/resource_exists.tf",
    "content": "## Setup Helper\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"foobar\"\n}\n\n# Fail\nresource \"aws_iam_user_policy\" \"resource_exists\" {\n  user = aws_iam_user.test_user.name\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"ec2:Describe*\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy/exists/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_USER_POLICY\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy_attachment/exists/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IAM_USER_POLICY_ATTACHMENT\n    message: IAM user should not have policies attached (make user a member of a group instead)\n    resource: aws_iam_user_policy_attachment\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n    tags:\n      - iam\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy_attachment/exists/tests/terraform11/resource_exists.tf",
    "content": "## Setup Helper\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"test-user\"\n}\n\nresource \"aws_iam_policy\" \"test_policy\" {\n  name   = \"test-policy\"\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_user_policy_attachment\" \"resource_exists\" {\n  user       = \"${aws_iam_user.user.name}\"\n  policy_arn = \"${aws_iam_policy.policy.arn}\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy_attachment/exists/tests/terraform12/resource_exists.tf",
    "content": "## Setup Helper\nresource \"aws_iam_user\" \"test_user\" {\n  name = \"test-user\"\n}\n\nresource \"aws_iam_policy\" \"test_policy\" {\n  name   = \"test-policy\"\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"rds:AddRoleToDBCluster\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"arn:aws:rds:us-east-1:1234567890:cluster:foo_cluster\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_iam_user_policy_attachment\" \"resource_exists\" {\n  user       = aws_iam_user.user.name\n  policy_arn = aws_iam_policy.policy.arn\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iam/iam_user_policy_attachment/exists/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IAM_USER_POLICY_ATTACHMENT\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/iot/iot_policy/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: IOT_WILDCARD_PRINCIPAL\n    message: IOT allow policy should not use a wildcard princpal\n    resource: aws_iot_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - iot\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/iot/iot_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "content": "# Test that IOT allow statement is not using a wildcard principal\n# https://www.terraform.io/docs/providers/aws/r/iot_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Allow with no wildcard principal\nresource \"aws_iot_policy\" \"iot_allow_no_wildcard\" {\n  name = \"PubSubToAnyTopic\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"iot:*\"\n      ],\n      \"Principal\": \"arn:aws:iam::1234567890:user/foo\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# PASS: Deny with no wildcard principal\nresource \"aws_iot_policy\" \"iot_deny_no_wildcard\" {\n  name = \"PubSubToAnyTopic\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"iot:*\"\n      ],\n      \"Principal\": \"arn:aws:iam::1234567890:user/foo\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# PASS: Deny with a wildcard principal\nresource \"aws_iot_policy\" \"iot_deny_with_wildcard\" {\n  name = \"PubSubToAnyTopic\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"iot:*\"\n      ],\n      \"Principal\": \"arn:aws:iam::1234567890:user/*\",\n      \"Effect\": \"Deny\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# FAIL: Allow with wildcard principal\nresource \"aws_iot_policy\" \"iot_allow_with_wildcard\" {\n  name = \"PubSubToAnyTopic\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"iot:*\"\n      ],\n      \"Principal\": \"arn:aws:iam::1234567890:user/*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# FAIL: Allow with wildcard principal\nresource \"aws_iot_policy\" \"iot_allow_principal_is_wildcard\" {\n  name = \"PubSubToAnyTopic\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": [\n        \"iot:*\"\n      ],\n      \"Principal\": \"*\",\n      \"Effect\": \"Allow\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/iot/iot_policy/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: IOT_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: KINESIS_STREAM_ENCRYPTION\n    message: Kinesis streams should specify the encryption type as KMS\n    resource: aws_kinesis_stream\n    severity: FAILURE\n    assertions:\n      - key: encryption_type\n        op: eq\n        value: KMS\n    tags:\n      - kinesis\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_encryption/tests/terraform11/encryption.tf",
    "content": "# Pass\nresource \"aws_kinesis_stream\" \"encryption_type_set_to_kms\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n}\n\n# Fail\nresource \"aws_kinesis_stream\" \"encryption_type_set_to_none\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"NONE\"\n}\n\n# Fail\nresource \"aws_kinesis_stream\" \"encryption_type_not_set\" {\n  name        = \"foo\"\n  shard_count = 1\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_encryption/tests/terraform12/encryption.tf",
    "content": "# Pass\nresource \"aws_kinesis_stream\" \"encryption_type_set_to_kms\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n}\n\n# Fail\nresource \"aws_kinesis_stream\" \"encryption_type_set_to_none\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"NONE\"\n}\n\n# Fail\nresource \"aws_kinesis_stream\" \"encryption_type_not_set\" {\n  name        = \"foo\"\n  shard_count = 1\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: KINESIS_STREAM_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_kms_key/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: KINESIS_STREAM_KMS\n    message: Kinesis streams should be associated with a kms key\n    resource: aws_kinesis_stream\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - kinesis\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_kms_key/tests/terraform11/kms_key.tf",
    "content": "# Pass\nresource \"aws_kinesis_stream\" \"kms_key_id_is_set\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n  kms_key_id      = \"alias/aws/kinesis\"\n}\n\n# Warn\nresource \"aws_kinesis_stream\" \"kms_key_id_is_not_set\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_kms_key/tests/terraform12/kms_key.tf",
    "content": "# Pass\nresource \"aws_kinesis_stream\" \"kms_key_id_is_set\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n  kms_key_id      = \"alias/aws/kinesis\"\n}\n\n# Warn\nresource \"aws_kinesis_stream\" \"kms_key_id_is_not_set\" {\n  name            = \"foo\"\n  shard_count     = 1\n  encryption_type = \"KMS\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis/kinesis_stream/kinesis_stream_kms_key/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: KINESIS_STREAM_KMS\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis_firehouse/kinesis_firehose_delivery_stream/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: KINESIS_FIREHOSE_DELIVERY_STREAM_ENCRYPTION\n    message: KinesisFirehoseDeliveryStream should use encrytion\n    resource: aws_kinesis_firehose_delivery_stream\n    severity: FAILURE\n    assertions:\n      # Either `kinesis_source_configuration` or `server_side_encryption` should be present\n      # but not both. Server-side encryption should not be enabled when a kinesis stream is\n      # configured as the source of the firehose delivery stream.\n      - xor:\n        - key: kinesis_source_configuration\n          op: present\n        - key: server_side_encryption\n          op: present\n      # Every time 'server_side_encryption' is present then is needs to be set to True\n      - every:\n          key: server_side_encryption\n          expressions:\n            - key: enabled\n              op: is-true\n      # Every time 's3_configuration' is present then is needs to be set to True\n      - every:\n          key: s3_configuration\n          expressions:\n            - key: kms_key_arn\n              op: present\n      # Every time 'extended_s3_configuration' is present then is needs to be set to True\n      - every:\n          key: extended_s3_configuration\n          expressions:\n            - key: kms_key_arn\n              op: present\n    tags:\n      - firehose\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis_firehouse/kinesis_firehose_delivery_stream/encryption/tests/terraform11/encryption.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_firehose_role\" {\n  name = \"test_firehose_role\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"firehose.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_kinesis_stream\" \"test_stream\" {\n  name        = \"test_stream\"\n  shard_count = 1\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"extended_s3_configuration_kms_key_arn_is_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"extended_s3_configuration_kms_key_arn_is_not_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn   = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn = \"${aws_s3_bucket.test_bucket.arn}\"\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"s3_configuration_kms_key_arn_is_set\" {\n  name        = \"terraform-kinesis-firehose-test-stream\"\n  destination = \"s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"s3_configuration_kms_key_arn_is_not_set\" {\n  name        = \"terraform-kinesis-firehose-test-stream\"\n  destination = \"s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  s3_configuration {\n    role_arn   = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn = \"${aws_s3_bucket.test_bucket.arn}\"\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"kinesis_source_configuration_is_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  kinesis_source_configuration {\n    kinesis_stream_arn = \"${aws_kinesis_stream.test_stream.arn}\"\n    role_arn           = \"${aws_iam_role.test_firehose_role.arn}\"\n  }\n\n  extended_s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_set_to_true\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_set_to_false\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = false\n  }\n\n  extended_s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_not_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n  }\n\n  extended_s3_configuration {\n    role_arn    = \"${aws_iam_role.test_firehose_role.arn}\"\n    bucket_arn  = \"${aws_s3_bucket.test_bucket.arn}\"\n    kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis_firehouse/kinesis_firehose_delivery_stream/encryption/tests/terraform12/encryption.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\nresource \"aws_iam_role\" \"test_firehose_role\" {\n  name = \"test_firehose_role\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"firehose.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_kinesis_stream\" \"test_stream\" {\n  name        = \"test_stream\"\n  shard_count = 1\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"extended_s3_configuration_kms_key_arn_is_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"extended_s3_configuration_kms_key_arn_is_not_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn   = aws_iam_role.test_firehose_role.arn\n    bucket_arn = aws_s3_bucket.test_bucket.arn\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"s3_configuration_kms_key_arn_is_set\" {\n  name        = \"terraform-kinesis-firehose-test-stream\"\n  destination = \"s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"s3_configuration_kms_key_arn_is_not_set\" {\n  name        = \"terraform-kinesis-firehose-test-stream\"\n  destination = \"s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  s3_configuration {\n    role_arn   = aws_iam_role.test_firehose_role.arn\n    bucket_arn = aws_s3_bucket.test_bucket.arn\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"kinesis_source_configuration_is_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  kinesis_source_configuration {\n    kinesis_stream_arn = aws_kinesis_stream.test_stream.arn\n    role_arn           = aws_iam_role.test_firehose_role.arn\n  }\n\n  extended_s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n\n# Pass\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_set_to_true\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = true\n  }\n\n  extended_s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_set_to_false\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n    enabled = false\n  }\n\n  extended_s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n\n# Fail\nresource \"aws_kinesis_firehose_delivery_stream\" \"server_side_encryption_enabled_not_set\" {\n  name        = \"foo\"\n  destination = \"extended_s3\"\n\n  server_side_encryption {\n  }\n\n  extended_s3_configuration {\n    role_arn    = aws_iam_role.test_firehose_role.arn\n    bucket_arn  = aws_s3_bucket.test_bucket.arn\n    kms_key_arn = aws_kms_key.test_key.arn\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kinesis_firehouse/kinesis_firehose_delivery_stream/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: KINESIS_FIREHOSE_DELIVERY_STREAM_ENCRYPTION\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/rotation/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: AWS_KMS_KEY_ROTATION\n    message: AWS KMS Key Rotation should be enabled\n    resource: aws_kms_key\n    severity: WARNING\n    assertions:\n      - key: enable_key_rotation\n        op: is-true\n    tags:\n      - kms\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/rotation/tests/terraform11/rotation.tf",
    "content": "# Pass\nresource \"aws_kms_key\" \"enable_key_rotation_set_to_true\" {\n  enable_key_rotation = true\n}\n\n# Warn\nresource \"aws_kms_key\" \"enable_key_rotation_set_to_false\" {\n  enable_key_rotation = false\n}\n\n# Warn\nresource \"aws_kms_key\" \"enable_key_rotation_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/rotation/tests/terraform12/rotation.tf",
    "content": "# Pass\nresource \"aws_kms_key\" \"enable_key_rotation_set_to_true\" {\n  enable_key_rotation = true\n}\n\n# Warn\nresource \"aws_kms_key\" \"enable_key_rotation_set_to_false\" {\n  enable_key_rotation = false\n}\n\n# Warn\nresource \"aws_kms_key\" \"enable_key_rotation_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/rotation/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: AWS_KMS_KEY_ROTATION\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/wildcard_policy/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: KMS_POLICY_WILDCARD_PRINCIPAL\n    message: KMS key allow policy should not use a wildcard princpal\n    resource: aws_kms_key\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - kms\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/wildcard_policy/tests/terraform12/wildcard_policy.tf",
    "content": "# Test that a KMS key policy allow is not using a wildcard for the principal\n# https://www.terraform.io/docs/providers/aws/r/kms_key.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: KMS key policy is an allow not using a wildcard principal\nresource \"aws_kms_key\" \"kms_key_allow_no_wildcard\" {\n  description             = \"Example Key\"\n  deletion_window_in_days = 10\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"Enable IAM User Permissions\",\n        \"Effect\": \"Allow\",\n        \"Principal\": {\n            \"AWS\": \"arn:aws:iam::1234567890:user/foo\"\n        },\n        \"Action\": \"kms:*\",\n        \"Resource\": \"*\"\n    }]\n}\nEOF\n}\n\n\n# PASS: KMS key policy is deny not using a wildcard principal\nresource \"aws_kms_key\" \"kms_key_deny_without_wildcard\" {\n  description             = \"Example Key\"\n  deletion_window_in_days = 10\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"Enable IAM User Permissions\",\n        \"Effect\": \"Deny\",\n        \"Principal\": {\n            \"AWS\": \"arn:aws:iam::1234567890:user\"\n        },\n        \"Action\": \"kms:*\",\n        \"Resource\": \"*\"\n    }]\n}\nEOF\n}\n\n# PASS: KMS key policy is deny using a wildcard principal\nresource \"aws_kms_key\" \"kms_key_deny_with_wildcard\" {\n  description             = \"Example Key\"\n  deletion_window_in_days = 10\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"Enable IAM User Permissions\",\n        \"Effect\": \"Deny\",\n        \"Principal\": {\n            \"AWS\": \"arn:aws:iam::1234567890:user/*\"\n        },\n        \"Action\": \"kms:*\",\n        \"Resource\": \"*\"\n    }]\n}\nEOF\n}\n\n# FAIL: KMS key policy is an allow using a wildcard principal\nresource \"aws_kms_key\" \"kms_key_allow_with_wildcard\" {\n  description             = \"Example Key\"\n  deletion_window_in_days = 10\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"Enable IAM User Permissions\",\n        \"Effect\": \"Allow\",\n        \"Principal\": {\n            \"AWS\": \"arn:aws:iam::1234567890:user/*\"\n        },\n        \"Action\": \"kms:*\",\n        \"Resource\": \"*\"\n    }]\n}\nEOF\n}\n\n# FAIL: KMS key policy is an allow using a wildcard principal\nresource \"aws_kms_key\" \"kms_key_principal_is_wildcard\" {\n  description             = \"Example Key\"\n  deletion_window_in_days = 10\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [{\n        \"Sid\": \"Enable IAM User Permissions\",\n        \"Effect\": \"Allow\",\n        \"Principal\": {\n            \"AWS\": \"*\"\n        },\n        \"Action\": \"kms:*\",\n        \"Resource\": \"*\"\n    }]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/kms/kms_key/wildcard_policy/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: KMS_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LAMBDA_FUNCTION_ENCRYPTION\n    message: Lambda function should specify kms_key_arn to use a non-default service key\n    resource: aws_lambda_function\n    severity: WARNING\n    assertions:\n      - key: kms_key_arn\n        op: present\n    tags:\n      - lambda\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/encryption/tests/terraform11/encryption.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\n# Pass\nresource \"aws_lambda_function\" \"kms_key_arn_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n}\n\n# Warn\nresource \"aws_lambda_function\" \"kms_key_arn_not_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/encryption/tests/terraform12/encryption.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\n# Pass\nresource \"aws_lambda_function\" \"kms_key_arn_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = aws_iam_role.test_role.arn\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = aws_kms_key.test_key.arn\n}\n\n# Warn\nresource \"aws_lambda_function\" \"kms_key_arn_not_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = aws_iam_role.test_role.arn\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: LAMBDA_FUNCTION_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/environment_variables_aws_secrets/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LAMBDA_ENVIRONMENT_SECRETS\n    message: Environment for Lambda function should not include AWS secrets\n    resource: aws_lambda_function\n    severity: FAILURE\n    # this rule fails if it finds a regex match for either the Access Key ID and/or the Secret Access Key\n    assertions:\n      - not:\n        - some:\n            key: \"environment[].variables[]|[0]|values(@)\"\n            expressions:\n              # Check if the string starts with any known 4 character ACCESS_KEY sequence\n              # and is 20 capital alpha-numeric characters long in total\n              - key: \"@\"\n                op: regex\n                value: \"^(A3T[A-Z0-9]|AKIA|AGPA|AIDA|AROA|AIPA|ANPA|ANVA|ASIA)[A-Z0-9]{16}$\"\n        - some:\n            key: \"environment[].variables[]|[0]|values(@)\"\n            expressions:\n              - and:\n                # Check if the string is exactly 40 characters long\n                - key: \"@\"\n                  op: regex\n                  value: \"^.{40}$\"\n                # Check if the string contains only alpha-numeric-slash-plus characters with at least 1 / or +\n                - key: \"@\"\n                  op: regex\n                  value: \"^[a-zA-Z0-9/+]+[/+]+[a-zA-Z0-9/+]+$\"\n    tags:\n      - lambda\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/environment_variables_aws_secrets/tests/terraform11/environment_variables_aws_secrets.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"bar\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_20_character_capital_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AXYZIOSFODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_21_character_capital_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFOODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_40_character_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFEMI_K7MDENG=bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_41_character_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFOEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_secret_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_access_key_and_secret_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFODNN7EXAMPLE\"\n      bar = \"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/environment_variables_aws_secrets/tests/terraform12/environment_variables_aws_secrets.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"bar\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_20_character_capital_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AXYZIOSFODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_21_character_capital_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFOODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_40_character_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFEMI_K7MDENG=bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Pass\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_not_set_41_character_string\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFOEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFODNN7EXAMPLE\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_secret_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n\n# Fail\nresource \"aws_lambda_function\" \"environment_variables_aws_secrets_access_key_and_secret_access_key_set\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n\n  environment {\n    variables = {\n      foo = \"AKIAIOSFODNN7EXAMPLE\"\n      bar = \"wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY\"\n    }\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_function/environment_variables_aws_secrets/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: LAMBDA_ENVIRONMENT_SECRETS\n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/action/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LAMBDA_PERMISSION_INVOKE_ACTION\n    message: Lambda permission should allow only lambda:InvokeAction\n    resource: aws_lambda_permission\n    severity: WARNING\n    assertions:\n      - key: action\n        op: eq\n        value: lambda:InvokeFunction\n    tags:\n      - lambda\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/action/tests/terraform11/action.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_lambda_function\" \"test_lambda\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  name = \"test_role\"\n\n  assume_role_policy = <<EOF\n {\n   \"Version\": \"2012-10-17\",\n   \"Statement\": [\n     {\n       \"Effect\": \"Allow\",\n       \"Action\": \"sts:AssumeRole\",\n       \"Principal\": {\n         \"Service\": \"lambda.amazonaws.com\"\n       }\n     }\n   ]\n }\n EOF\n}\n\n# Pass\nresource \"aws_lambda_permission\" \"action_invokefunction_set\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n# Warn\nresource \"aws_lambda_permission\" \"action_invokefunction_not_set\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:GetFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/action/tests/terraform12/action.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_lambda_function\" \"test_lambda\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = aws_iam_role.test_role.arn\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = aws_kms_key.test_key.arn\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  name = \"test_role\"\n\n  assume_role_policy = <<EOF\n {\n   \"Version\": \"2012-10-17\",\n   \"Statement\": [\n     {\n       \"Effect\": \"Allow\",\n       \"Action\": \"sts:AssumeRole\",\n       \"Principal\": {\n         \"Service\": \"lambda.amazonaws.com\"\n       }\n     }\n   ]\n }\n EOF\n}\n\n# Pass\nresource \"aws_lambda_permission\" \"action_invokefunction_set\" {\n  function_name = aws_lambda_function.test_lambda.function_name\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n# Warn\nresource \"aws_lambda_permission\" \"action_invokefunction_not_set\" {\n  function_name = aws_lambda_function.test_lambda.function_name\n  action        = \"lambda:GetFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/action/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: LAMBDA_PERMISSION_INVOKE_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/principal_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: LAMBDA_PERMISSION_WILDCARD_PRINCIPAL\n    message: Lambda permission should not have wildcard principal\n    resource: aws_lambda_permission\n    severity: FAILURE\n    assertions:\n      - key: principal\n        op: does-not-contain\n        value: \"*\"\n    tags:\n      - lambda\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/principal_wildcard/tests/terraform11/principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_lambda_function\" \"test_lambda\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  name = \"test_role\"\n\n  assume_role_policy = <<EOF\n {\n   \"Version\": \"2012-10-17\",\n   \"Statement\": [\n     {\n       \"Effect\": \"Allow\",\n       \"Action\": \"sts:AssumeRole\",\n       \"Principal\": {\n         \"Service\": \"lambda.amazonaws.com\"\n       }\n     }\n   ]\n }\n EOF\n}\n\n# Pass\nresource \"aws_lambda_permission\" \"principal_without_wildcard\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n# Fail\nresource \"aws_lambda_permission\" \"principal_with_wildcard\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"*\"\n}\n\n# Fail\nresource \"aws_lambda_permission\" \"principal_with_wildcard_prefix\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"*.amazonaws.com\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/principal_wildcard/tests/terraform12/principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_lambda_function\" \"test_lambda\" {\n  filename      = \"lambdatest.zip\"\n  function_name = \"foobar\"\n  role          = \"${aws_iam_role.test_role.arn}\"\n  handler       = \"exports.handler\"\n  runtime       = \"nodejs8.10\"\n  kms_key_arn   = \"${aws_kms_key.test_key.arn}\"\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  name = \"test_role\"\n\n  assume_role_policy = <<EOF\n {\n   \"Version\": \"2012-10-17\",\n   \"Statement\": [\n     {\n       \"Effect\": \"Allow\",\n       \"Action\": \"sts:AssumeRole\",\n       \"Principal\": {\n         \"Service\": \"lambda.amazonaws.com\"\n       }\n     }\n   ]\n }\n EOF\n}\n\n# Pass\nresource \"aws_lambda_permission\" \"principal_without_wildcard\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"events.amazonaws.com\"\n}\n\n# Fail\nresource \"aws_lambda_permission\" \"principal_with_wildcard\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"*\"\n}\n\n# Fail\nresource \"aws_lambda_permission\" \"principal_with_wildcard_prefix\" {\n  function_name = \"${aws_lambda_function.test_lambda.function_name}\"\n  action        = \"lambda:InvokeFunction\"\n  principal     = \"*.amazonaws.com\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/lambda/lambda_permission/principal_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: LAMBDA_PERMISSION_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/mediastore/media_store_container_policy/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: MEDIASTORE_CONTAINER_WILDCARD_PRINCIPAL\n    message: MediaStore container allow policy should not use a wildcard princpal\n    resource: aws_media_store_container_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - mediastore\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/mediastore/media_store_container_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "content": "# Test that a MediaStore container policy does not use a wildcard in the principal when allow actions\n# https://www.terraform.io/docs/providers/aws/r/media_store_container_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Allow policy with no wildcard principal\nresource \"aws_media_store_container_policy\" \"msc_allow_no_wildcard\" {\n  container_name = \"example\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"mediastore:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890098:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:mediastore:1234567890098:us-east-1:container/example/*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny policy with no wildcard principal\nresource \"aws_media_store_container_policy\" \"msc_deny_no_wildcard\" {\n  container_name = \"example\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"mediastore:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890098:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:mediastore:1234567890098:us-east-1:container/example/*\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny policy with wildcard principal\nresource \"aws_media_store_container_policy\" \"msc_deny_wildcard\" {\n  container_name = \"example\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"mediastore:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890098:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:mediastore:1234567890098:us-east-1:container/example/*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow policy with wildcard in principal\nresource \"aws_media_store_container_policy\" \"msc_allow_with_wildcard\" {\n  container_name = \"example\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"mediastore:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890098:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:mediastore:1234567890098:us-east-1:container/example/*\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow policy principal is a wildcard\nresource \"aws_media_store_container_policy\" \"msc_allow_principal_is_wildcard\" {\n  container_name = \"example\"\n\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"mediastore:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:mediastore:1234567890098:us-east-1:container/example/*\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/mediastore/media_store_container_policy/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: MEDIASTORE_CONTAINER_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/neptune/neptune_cluster/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: NEPTUNE_DB_ENCRYPTION\n    message: Neptune database cluster storage should have encryption enabled\n    resource: aws_neptune_cluster\n    severity: FAILURE\n    assertions:\n    - key: storage_encrypted\n      op: is-true\n    tags:\n      - neptune\n\n  - id: NEPTUNE_DB_KMS\n    message: Neptune database cluster storage should have a KMS key specified\n    resource: aws_neptune_cluster\n    severity: WARNING\n    assertions:\n    - key: kms_key_arn\n      op: present\n    tags:\n      - neptune\n"
  },
  {
    "path": "cli/assets/terraform/aws/neptune/neptune_cluster/encryption/tests/terraform12/encryption.tf",
    "content": "# Test that Neptune cluster has encryption enablded and KMS\n# https://www.terraform.io/docs/providers/aws/r/neptune_cluster.html#storage_encrypted\n# https://www.terraform.io/docs/providers/aws/r/neptune_cluster.html#kms_key_arn\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: Encryption is enabled with KMS\nresource \"aws_neptune_cluster\" \"storage_encrypted_set_to_true\" {\n  storage_encrypted = true\n  kms_key_arn       = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# WARN: Encryption is enabled without KMS\nresource \"aws_neptune_cluster\" \"storage_encrypted_set_to_true_no_kms\" {\n  storage_encrypted = true\n}\n\n# FAIL: Encryption is disabled\n# WARN: KMS key is not specified\nresource \"aws_neptune_cluster\" \"storage_encrypted_set_to_false\" {\n  storage_encrypted = false\n}\n\n# FAIL: Encryption is not enabled\n# WARN: KMS key is not specified\nresource \"aws_neptune_cluster\" \"storage_encrypted_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/neptune/neptune_cluster/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: NEPTUNE_DB_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: NEPTUNE_DB_KMS\n    warnings: 3\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/opsworks/opsworks_application/require_ssl/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: OPSWORKS_APP_SSL\n    message: OpsWorks Applications must use SSL\n    resource: aws_opsworks_application\n    severity: FAILURE\n    assertions:\n      - key: enable_ssl\n        op: is-true\n    tags:\n      - opsworks"
  },
  {
    "path": "cli/assets/terraform/aws/opsworks/opsworks_application/require_ssl/tests/terraform12/require_ssl.tf",
    "content": "# Test that an OpsWorks application has SSL enabled\n# https://www.terraform.io/docs/providers/aws/r/opsworks_application.html#enable_ssl\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: SSL is enabled\nresource \"aws_opsworks_application\" \"opsworks_app_ssl_enabled\" {\n  name        = \"foobar application\"\n  short_name  = \"foobar\"\n  stack_id    = \"ExampleStackID\"\n  type        = \"rails\"\n  description = \"This is a Rails application\"\n\n  domains = [\n    \"example.com\",\n    \"sub.example.com\",\n  ]\n\n  environment {\n    key    = \"key\"\n    value  = \"value\"\n    secure = false\n  }\n\n  app_source {\n    type     = \"git\"\n    revision = \"master\"\n    url      = \"https://github.com/example.git\"\n  }\n\n  enable_ssl = true\n\n  ssl_configuration {\n    private_key = \"example.key\"\n    certificate = \"example.crt\"\n  }\n\n  document_root         = \"public\"\n  auto_bundle_on_deploy = true\n  rails_env             = \"staging\"\n}\n\n# FAIL: SSL is not enabled\nresource \"aws_opsworks_application\" \"opsworks_app_ssl_disabled\" {\n  name        = \"foobar application\"\n  short_name  = \"foobar\"\n  stack_id    = \"ExampleStackID\"\n  type        = \"rails\"\n  description = \"This is a Rails application\"\n\n  domains = [\n    \"example.com\",\n    \"sub.example.com\",\n  ]\n\n  environment {\n    key    = \"key\"\n    value  = \"value\"\n    secure = false\n  }\n\n  app_source {\n    type     = \"git\"\n    revision = \"master\"\n    url      = \"https://github.com/example.git\"\n  }\n\n  enable_ssl = false\n\n  document_root         = \"public\"\n  auto_bundle_on_deploy = true\n  rails_env             = \"staging\"\n}\n\n# FAIL: enable_ssl is not defined\nresource \"aws_opsworks_application\" \"opsworks_app_enable_ssl_not_defined\" {\n  name        = \"foobar application\"\n  short_name  = \"foobar\"\n  stack_id    = \"ExampleStackID\"\n  type        = \"rails\"\n  description = \"This is a Rails application\"\n\n  domains = [\n    \"example.com\",\n    \"sub.example.com\",\n  ]\n\n  environment {\n    key    = \"key\"\n    value  = \"value\"\n    secure = false\n  }\n\n  app_source {\n    type     = \"git\"\n    revision = \"master\"\n    url      = \"https://github.com/example.git\"\n  }\n\n  document_root         = \"public\"\n  auto_bundle_on_deploy = true\n  rails_env             = \"staging\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/opsworks/opsworks_application/require_ssl/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: OPSWORKS_APP_SSL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: DB_INSTANCE_ENCRYPTION\n    message: DB Instance should have storage_encrypted set to True\n    resource: aws_db_instance\n    severity: FAILURE\n    assertions:\n      - or:\n          - key: storage_encrypted\n            op: is-true\n          - key: replicate_source_db\n            op: present\n    tags:\n      - rds\n  - id: REPLICA_DB_INSTANCE_ENCRYPTION\n    message: Replica DB Instance should specify a KMS Key\n    resource: aws_db_instance\n    severity: WARNING\n    assertions:\n      - or:\n        - key: storage_encrypted\n          op: present\n        - and:\n          - key: replicate_source_db\n            op: present\n          - key: kms_key_id\n            op: present\n    tags:\n      - rds\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/encryption/tests/terraform11/storage_encryption.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"storage_encrypted_set_to_true\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = \"foo\"\n  password          = \"foobarbaz\"\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"storage_encrypted_set_to_false\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = \"foo\"\n  password          = \"foobarbaz\"\n  storage_encrypted = false\n}\n\n# Fail\nresource \"aws_db_instance\" \"storage_encrypted_not_set\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = \"foo\"\n  password          = \"foobarbaz\"\n}\n\n# Pass\nresource \"aws_db_instance\" \"replicate_source_db_is_set\" {\n  instance_class      = \"db.t2.micro\"\n  replicate_source_db = \"foo\"\n  kms_key_id          = \"${aws_kms_key.test_key.id}\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/encryption/tests/terraform12/storage_encryption.tf",
    "content": "## Setup Helper\nvariable \"test_db_username\" {\n  default = \"foo\"\n}\n\nvariable \"test_db_password\" {\n  default = \"foobarbaz\"\n}\n\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"storage_encrypted_set_to_true\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = var.test_db_username\n  password          = var.test_db_password\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"storage_encrypted_set_to_false\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = var.test_db_username\n  password          = var.test_db_password\n  storage_encrypted = false\n}\n\n# Fail\nresource \"aws_db_instance\" \"storage_encrypted_not_set\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = var.test_db_username\n  password          = var.test_db_password\n}\n\n# Pass\nresource \"aws_db_instance\" \"replicate_source_db_is_set_with_kms_key_id\" {\n  instance_class      = \"db.t2.micro\"\n  replicate_source_db = \"foo\"\n  kms_key_id          = aws_kms_key.test_key.id\n}\n\n# Warn\nresource \"aws_db_instance\" \"replicate_source_db_is_set_without_kms_key_id\" {\n  instance_class      = \"db.t2.micro\"\n  replicate_source_db = \"foo\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: DB_INSTANCE_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n  -\n    ruleId: REPLICA_DB_INSTANCE_ENCRYPTION \n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n  -\n    ruleId: REPLICA_DB_INSTANCE_ENCRYPTION \n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/publicly_accessible/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: RDS_PUBLIC_AVAILABILITY\n    message: RDS instance should not be publicly accessible\n    resource: aws_db_instance\n    severity: FAILURE\n    assertions:\n    - not:\n      - key: publicly_accessible\n        op: is-true\n    tags:\n      - rds\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/publicly_accessible/tests/terraform11/publicly_accessible.tf",
    "content": "# Pass\nresource \"aws_db_instance\" \"publicly_accessible_not_set\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = \"foo\"\n  password          = \"foobarbaz\"\n  storage_encrypted = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"publicly_accessible_set_to_false\" {\n  allocated_storage   = 20\n  engine              = \"mysql\"\n  engine_version      = \"5.7\"\n  instance_class      = \"db.t2.micro\"\n  username            = \"foo\"\n  password            = \"foobarbaz\"\n  storage_encrypted   = true\n  publicly_accessible = false\n}\n\n# Fail\nresource \"aws_db_instance\" \"publicly_accessible_set_to_true\" {\n  allocated_storage   = 20\n  engine              = \"mysql\"\n  engine_version      = \"5.7\"\n  instance_class      = \"db.t2.micro\"\n  username            = \"foo\"\n  password            = \"foobarbaz\"\n  storage_encrypted   = true\n  publicly_accessible = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/publicly_accessible/tests/terraform12/publicly_accessible.tf",
    "content": "## Setup Helper\nvariable \"test_db_username\" {\n  default = \"foo\"\n}\n\nvariable \"test_db_password\" {\n  default = \"foobarbaz\"\n}\n\n# Pass\nresource \"aws_db_instance\" \"publicly_accessible_not_set\" {\n  allocated_storage = 20\n  engine            = \"mysql\"\n  engine_version    = \"5.7\"\n  instance_class    = \"db.t2.micro\"\n  username          = var.test_db_username\n  password          = var.test_db_password\n  storage_encrypted = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"publicly_accessible_set_to_false\" {\n  allocated_storage   = 20\n  engine              = \"mysql\"\n  engine_version      = \"5.7\"\n  instance_class      = \"db.t2.micro\"\n  username            = var.test_db_username\n  password            = var.test_db_password\n  storage_encrypted   = true\n  publicly_accessible = false\n}\n\n# Fail\nresource \"aws_db_instance\" \"publicly_accessible_set_to_true\" {\n  allocated_storage   = 20\n  engine              = \"mysql\"\n  engine_version      = \"5.7\"\n  instance_class      = \"db.t2.micro\"\n  username            = var.test_db_username\n  password            = var.test_db_password\n  storage_encrypted   = true\n  publicly_accessible = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/db_instance/publicly_accessible/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: RDS_PUBLIC_AVAILABILITY\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/rds_cluster/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: RDS_CLUSTER_ENCYPTION\n    message: RDS Cluster should have storage_encrypted set to True\n    resource: aws_rds_cluster\n    severity: FAILURE\n    assertions:\n      - or:\n        - key: storage_encrypted\n          op: is-true\n        # serverless engine_mode has encryption enabled by default\n        - and:\n          - key: engine_mode\n            op: eq\n            value: serverless\n          - key: storage_encrypted\n            op: absent\n    tags:\n      - rds\n\n  - id: RDS_CLUSTER_ENCYPTION_KMS\n    message: RDS Cluster should have a KMS key when storage_encrypted is enabled\n    resource: aws_rds_cluster\n    severity: WARNING\n    assertions:\n      # If storage_encrypted is present and enabled, then kms_key_id should also be present\n      - xor:\n        - or:\n          - key: storage_encrypted\n            op: absent\n          - key: storage_encrypted\n            op: is-false\n        - and:\n          - key: storage_encrypted\n            op: present\n          - key: kms_key_id\n            op: present\n\n    tags:\n      - rds\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/rds_cluster/encryption/tests/terraform12/storage_encryption.tf",
    "content": "# Test that an RDS cluster has encryption endabled and a KMS key specified\n# https://www.terraform.io/docs/providers/aws/r/rds_cluster.html#storage_encrypted\n# https://www.terraform.io/docs/providers/aws/r/rds_cluster.html#kms_key_id\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: storage_encrypted enabled and kms_key_id specified\nresource \"aws_rds_cluster\" \"storage_encrypted_set_to_true\" {\n  engine            = \"aurora-mysql\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = true\n  kms_key_id        = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# FAIL: storage_encrypted disabled\nresource \"aws_rds_cluster\" \"storage_encrypted_set_to_false\" {\n  engine            = \"aurora-mysql\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = false\n}\n\n# FAIL: storage_encrypted not defined\nresource \"aws_rds_cluster\" \"storage_encrypted_not_set\" {\n  engine          = \"aurora-mysql\"\n  master_username = \"foo\"\n  master_password = \"bar\"\n}\n\n# PASS: serverless mode uses default encryption\nresource \"aws_rds_cluster\" \"serverless_engine_mode_encrypted_by_default\" {\n  engine          = \"aurora-mysql\"\n  engine_mode     = \"serverless\"\n  master_username = \"foo\"\n  master_password = \"bar\"\n}\n\n# PASS: serverless mode with encryption enabled and a kms key\nresource \"aws_rds_cluster\" \"serverless_engine_mode_with_storage_encrypted_set_to_true\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"serverless\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = true\n  kms_key_id        = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# WARN: serverless mode with encryption enabled but no kms key\nresource \"aws_rds_cluster\" \"serverless_engine_mode_with_storage_encrypted_set_to_true_no_kms\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"serverless\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = true\n}\n\n# FAIL: encryption is disabled\nresource \"aws_rds_cluster\" \"serverless_engine_mode_with_storage_encrypted_set_to_false\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"serverless\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = false\n}\n\n# FAIL: Encryption not enabled\nresource \"aws_rds_cluster\" \"provisioned_engine_mode_unencrypted_by_default\" {\n  engine          = \"aurora-mysql\"\n  engine_mode     = \"provisioned\"\n  master_username = \"foo\"\n  master_password = \"bar\"\n}\n\n# PASS: Encryption enabled with kms key\nresource \"aws_rds_cluster\" \"provisioned_engine_mode_with_storage_encrypted_set_to_true\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"provisioned\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = true\n  kms_key_id        = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# WARN: Encryption enabled without kms key\nresource \"aws_rds_cluster\" \"provisioned_engine_mode_with_storage_encrypted_set_to_true_no_kms\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"provisioned\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = true\n}\n\n# FAIL: Encrypytion disabled\nresource \"aws_rds_cluster\" \"provisioned_engine_mode_with_storage_encrypted_set_to_false\" {\n  engine            = \"aurora-mysql\"\n  engine_mode       = \"provisioned\"\n  master_username   = \"foo\"\n  master_password   = \"bar\"\n  storage_encrypted = false\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/rds/rds_cluster/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: RDS_CLUSTER_ENCYPTION\n    warnings: 0\n    failures: 5\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: RDS_CLUSTER_ENCYPTION_KMS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/encrypted/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_ENCRYPTION\n    message: RedshiftCluster should use encryption\n    resource: aws_redshift_cluster\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n    tags:\n      - redshift\n\n  - id: REDSHIFT_CLUSTER_ENCRYPTION_KMS\n    message: RedshiftCluster should have KMS key provided\n    resource: aws_redshift_cluster\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - redshift\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/encrypted/tests/terraform12/encrypted.tf",
    "content": "# Test that a Redshift cluster is encrypted with a KMS key\n# https://www.terraform.io/docs/providers/aws/r/redshift_cluster.html#encrypted\n# https://www.terraform.io/docs/providers/aws/r/redshift_cluster.html#kms_key_id\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# FAIL: Encryption disabled\n# WARN: No KMS key\nresource \"aws_redshift_cluster\" \"encrypted_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"F0obarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n}\n\n# FAIL: Encryption disabled\n# WARN: No KMS key\nresource \"aws_redshift_cluster\" \"encrypted_set_to_false\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"F0obarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = false\n}\n\n# PASS: Cluster encrypted with KMS key\nresource \"aws_redshift_cluster\" \"encrypted_set_to_true\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"F0obarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"arn:aws:kms:us-west-2:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab\"\n}\n\n# WARN: Cluster encrypted without KMS key\nresource \"aws_redshift_cluster\" \"encrypted_set_to_true_no_kms\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"F0obarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/encrypted/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_ENCRYPTION\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: REDSHIFT_CLUSTER_ENCRYPTION_KMS\n    warnings: 3\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/enhanced_vpc_routing/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_ENHANCED_VPC_ROUTING\n    message: RedshiftCluster should use enhanced vpc routing\n    resource: aws_redshift_cluster\n    severity: WARNING\n    assertions:\n      - key: enhanced_vpc_routing\n        op: is-true\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/enhanced_vpc_routing/tests/terraform11/enhanced_vpc_routing.tf",
    "content": "variable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"${var.kms_key_arn}\"\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_set_to_false\" {\n  cluster_identifier   = \"my-redshift-cluster\"\n  database_name        = \"mydb\"\n  master_username      = \"admin\"\n  master_password      = \"foobarbaz\"\n  node_type            = \"dc2.large\"\n  cluster_type         = \"single-node\"\n  encrypted            = true\n  kms_key_id           = \"${var.kms_key_arn}\"\n  enhanced_vpc_routing = false\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_set_to_true\" {\n  cluster_identifier   = \"my-redshift-cluster\"\n  database_name        = \"mydb\"\n  master_username      = \"admin\"\n  master_password      = \"foobarbaz\"\n  node_type            = \"dc2.large\"\n  cluster_type         = \"single-node\"\n  encrypted            = true\n  kms_key_id           = \"${var.kms_key_arn}\"\n  enhanced_vpc_routing = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/enhanced_vpc_routing/tests/terraform12/enhanced_vpc_routing.tf",
    "content": "variable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = var.kms_key_arn\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_set_to_false\" {\n  cluster_identifier   = \"my-redshift-cluster\"\n  database_name        = \"mydb\"\n  master_username      = \"admin\"\n  master_password      = \"foobarbaz\"\n  node_type            = \"dc2.large\"\n  cluster_type         = \"single-node\"\n  encrypted            = true\n  kms_key_id           = var.kms_key_arn\n  enhanced_vpc_routing = false\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"enhanced_vpc_routing_set_to_true\" {\n  cluster_identifier   = \"my-redshift-cluster\"\n  database_name        = \"mydb\"\n  master_username      = \"admin\"\n  master_password      = \"foobarbaz\"\n  node_type            = \"dc2.large\"\n  cluster_type         = \"single-node\"\n  encrypted            = true\n  kms_key_id           = var.kms_key_arn\n  enhanced_vpc_routing = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/enhanced_vpc_routing/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_ENHANCED_VPC_ROUTING\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/kms_key_id/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_KMS_KEY_ID\n    message: RedshiftCluster should specify kms_key_id\n    resource: aws_redshift_cluster\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - redshift\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/kms_key_id/tests/terraform11/kms_key_id.tf",
    "content": "variable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"kms_key_id_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"kms_key_id_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"kms_key_id_set_as_variable\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"${var.kms_key_arn}\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/kms_key_id/tests/terraform12/kms_key_id.tf",
    "content": "variable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"kms_key_id_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"kms_key_id_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"kms_key_id_set_as_variable\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = var.kms_key_arn\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/kms_key_id/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_KMS_KEY_ID\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/logging/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_AUDIT_LOGGING\n    message: RedshiftCluster should have audit logging enabled\n    resource: aws_redshift_cluster\n    severity: WARNING\n    assertions:\n      - key: logging\n        op: present\n      - every:\n          key: logging\n          expressions:\n            - key: enable\n              op: is-true\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/logging/tests/terraform11/logging.tf",
    "content": "# Setup\nvariable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\nresource \"aws_s3_bucket\" \"audit_logs\" {}\n\n\n# Warn\nresource \"aws_redshift_cluster\" \"logging_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"logging_is_disabled\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"${var.kms_key_arn}\"\n  logging {\n    enable        = false\n    bucket_name   = \"${aws_s3_bucket.audit_logs.id}\"\n    s3_key_prefix = \"aws_redshift_cluster\"\n  }\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"logging_is_enabled\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"${var.kms_key_arn}\"\n  logging {\n    enable        = true\n    bucket_name   = \"${aws_s3_bucket.audit_logs.id}\"\n    s3_key_prefix = \"aws_redshift_cluster\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/logging/tests/terraform12/logging.tf",
    "content": "# Setup\nvariable \"kms_key_arn\" {\n  default = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\nresource \"aws_s3_bucket\" \"audit_logs\" {}\n\n\n# Warn\nresource \"aws_redshift_cluster\" \"logging_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = \"arn:aws:kms:us-east-1:1234567890:key/foobar\"\n}\n\n# Warn\nresource \"aws_redshift_cluster\" \"logging_is_disabled\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = var.kms_key_arn\n  logging {\n    enable        = false\n    bucket_name   = aws_s3_bucket.audit_logs.id\n    s3_key_prefix = \"aws_redshift_cluster\"\n  }\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"logging_is_enabled\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n  encrypted          = true\n  kms_key_id         = var.kms_key_arn\n  logging {\n    enable        = true\n    bucket_name   = aws_s3_bucket.audit_logs.id\n    s3_key_prefix = \"aws_redshift_cluster\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/logging/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_AUDIT_LOGGING\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/publicly_accessible/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_PUBLICLY_ACCESSIBLE\n    message: RedshiftCluster should not be publicly accessible\n    resource: aws_redshift_cluster\n    severity: FAILURE\n    assertions:\n      - key: publicly_accessible\n        op: is-false\n    tags:\n      - redshift\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/publicly_accessible/tests/terraform11/publicly_accessible.tf",
    "content": "# Fail\nresource \"aws_redshift_cluster\" \"publicly_accessible_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n}\n\n# Fail\nresource \"aws_redshift_cluster\" \"publicly_accessible_set_to_true\" {\n  cluster_identifier  = \"my-redshift-cluster\"\n  database_name       = \"mydb\"\n  master_username     = \"admin\"\n  master_password     = \"foobarbaz\"\n  node_type           = \"dc2.large\"\n  cluster_type        = \"single-node\"\n  publicly_accessible = true\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"publicly_accessible_set_to_false\" {\n  cluster_identifier  = \"my-redshift-cluster\"\n  database_name       = \"mydb\"\n  master_username     = \"admin\"\n  master_password     = \"foobarbaz\"\n  node_type           = \"dc2.large\"\n  cluster_type        = \"single-node\"\n  publicly_accessible = false\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/publicly_accessible/tests/terraform12/publicly_accessible.tf",
    "content": "# Fail\nresource \"aws_redshift_cluster\" \"publicly_accessible_not_set\" {\n  cluster_identifier = \"my-redshift-cluster\"\n  database_name      = \"mydb\"\n  master_username    = \"admin\"\n  master_password    = \"foobarbaz\"\n  node_type          = \"dc2.large\"\n  cluster_type       = \"single-node\"\n}\n\n# Fail\nresource \"aws_redshift_cluster\" \"publicly_accessible_set_to_true\" {\n  cluster_identifier  = \"my-redshift-cluster\"\n  database_name       = \"mydb\"\n  master_username     = \"admin\"\n  master_password     = \"foobarbaz\"\n  node_type           = \"dc2.large\"\n  cluster_type        = \"single-node\"\n  publicly_accessible = true\n}\n\n# Pass\nresource \"aws_redshift_cluster\" \"publicly_accessible_set_to_false\" {\n  cluster_identifier  = \"my-redshift-cluster\"\n  database_name       = \"mydb\"\n  master_username     = \"admin\"\n  master_password     = \"foobarbaz\"\n  node_type           = \"dc2.large\"\n  cluster_type        = \"single-node\"\n  publicly_accessible = false\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_cluster/publicly_accessible/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_PUBLICLY_ACCESSIBLE\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/require_ssl/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_PARAMETER_GROUP_REQUIRE_SSL\n    message: RedshiftCluster Parameter Group should set require_ssl to true\n    resource: aws_redshift_parameter_group\n    severity: WARNING\n    assertions:\n      - exactly-one:\n          key: parameter\n          expressions:\n            - key: name\n              op: eq\n              value: require_ssl\n            - key: value\n              op: is-true\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/require_ssl/tests/terraform12/require_ssl.tf",
    "content": "# Test that require_ssl parameter is present and set to true\n# https://www.terraform.io/docs/providers/aws/r/redshift_parameter_group.html\n\n# WARN require_ssl is not set\nresource \"aws_redshift_parameter_group\" \"parameter_and_require_ssl_not_set\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n}\n\n# WARN: require_ssl is false\nresource \"aws_redshift_parameter_group\" \"require_ssl_set_to_false\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n\n  parameter {\n    name  = \"enable_user_activity_logging\"\n    value = \"true\"\n  }\n\n  parameter {\n    name  = \"require_ssl\"\n    value = \"false\"\n  }\n\n  parameter {\n    name  = \"query_group\"\n    value = \"example\"\n  }\n}\n\n# PASS: require_ssl is set to true\nresource \"aws_redshift_parameter_group\" \"require_ssl_set_to_true\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n\n  parameter {\n    name  = \"enable_user_activity_logging\"\n    value = \"true\"\n  }\n\n  parameter {\n    name  = \"require_ssl\"\n    value = \"true\"\n  }\n\n  parameter {\n    name  = \"query_group\"\n    value = \"example\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/require_ssl/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_PARAMETER_GROUP_REQUIRE_SSL\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/user_logging/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: REDSHIFT_CLUSTER_PARAMETER_GROUP_USER_ACTIVITY_LOGGING\n    message: RedshiftCluster Parameter Group should set enable_user_activity_logging to true\n    resource: aws_redshift_parameter_group\n    severity: FAILURE\n    assertions:\n      - exactly-one:\n          key: parameter\n          expressions:\n            - key: name\n              op: eq\n              value: enable_user_activity_logging\n            - key: value\n              op: is-true\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/user_logging/tests/terraform12/user_logging.tf",
    "content": "# Test that user activity logging is enabled\n# https://www.terraform.io/docs/providers/aws/r/redshift_parameter_group.html\n\n# FAIL: enable_user_activity_logging is not set\nresource \"aws_redshift_parameter_group\" \"logging_not_set\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n\n  parameter {\n    name  = \"require_ssl\"\n    value = \"true\"\n  }\n}\n\n# FAIL: enable_user_activity_logging is false\nresource \"aws_redshift_parameter_group\" \"logging_set_to_false\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n\n  parameter {\n    name  = \"require_ssl\"\n    value = \"false\"\n  }\n\n  parameter {\n    name  = \"enable_user_activity_logging\"\n    value = \"false\"\n  }\n\n  parameter {\n    name  = \"query_group\"\n    value = \"example\"\n  }\n}\n\n# PASS: enable_user_activity_logging is set to true\nresource \"aws_redshift_parameter_group\" \"logging_set_to_true\" {\n  name   = \"foobar\"\n  family = \"redshift-1.0\"\n\n  parameter {\n    name  = \"require_ssl\"\n    value = \"true\"\n  }\n\n  parameter {\n    name  = \"enable_user_activity_logging\"\n    value = \"true\"\n  }\n\n  parameter {\n    name  = \"query_group\"\n    value = \"example\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/redshift/redshift_parameter_group/user_logging/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: REDSHIFT_CLUSTER_PARAMETER_GROUP_USER_ACTIVITY_LOGGING\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/acl_not_public/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_ACL\n    message: S3 Bucket should not be public\n    resource: aws_s3_bucket\n    severity: FAILURE\n    assertions:\n      - key: acl\n        op: not-in\n        value: public-read,public-read-write\n    tags:\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/acl_not_public/tests/terraform11/acl_not_public.tf",
    "content": "# Pass\nresource \"aws_s3_bucket\" \"acl_not_set\" {\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_private\" {\n  acl = \"private\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_aws-exec-read\" {\n  acl = \"aws-exec-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_authenticated-read\" {\n  acl = \"authenticated-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_bucket-owner-read\" {\n  acl = \"bucket-owner-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_bucket-owner-full-control\" {\n  acl = \"bucket-owner-full-control\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_log-delivery-write\" {\n  acl = \"log-delivery-write\"\n}\n\n# Fail\nresource \"aws_s3_bucket\" \"acl_set_to_public-read\" {\n  acl = \"public-read\"\n}\n\n# Fail\nresource \"aws_s3_bucket\" \"acl_set_to_public-read-write\" {\n  acl = \"public-read-write\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/acl_not_public/tests/terraform12/acl_not_public.tf",
    "content": "# Pass\nresource \"aws_s3_bucket\" \"acl_not_set\" {\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_private\" {\n  acl = \"private\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_aws-exec-read\" {\n  acl = \"aws-exec-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_authenticated-read\" {\n  acl = \"authenticated-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_bucket-owner-read\" {\n  acl = \"bucket-owner-read\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_bucket-owner-full-control\" {\n  acl = \"bucket-owner-full-control\"\n}\n\n# Pass\nresource \"aws_s3_bucket\" \"acl_set_to_log-delivery-write\" {\n  acl = \"log-delivery-write\"\n}\n\n# Fail\nresource \"aws_s3_bucket\" \"acl_set_to_public-read\" {\n  acl = \"public-read\"\n}\n\n# Fail\nresource \"aws_s3_bucket\" \"acl_set_to_public-read-write\" {\n  acl = \"public-read-write\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/acl_not_public/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_ACL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/server_side_encryption_enabled/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_ENCRYPTION\n    message: S3 bucket should be encrypted\n    resource: aws_s3_bucket\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: server_side_encryption_configuration\n    tags:\n      - s3\n\n  - id: S3_BUCKET_KMS\n    message: S3 bucket should have a KMS key specified and sse_algorithm set to aws:kms\n    resource: aws_s3_bucket\n    severity: WARNING\n    assertions:\n      - every:\n          key: server_side_encryption_configuration[].rule[].apply_server_side_encryption_by_default\n          expressions:\n            - key: kms_master_key_id\n              op: present\n            - key: sse_algorithm\n              op: eq\n              value: \"aws:kms\"\n    tags:\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/server_side_encryption_enabled/tests/terraform12/server_side_encryption_enabled.tf",
    "content": "# Test that server side encryption is used with a KMS key and aws:kms algorithm\n# https://www.terraform.io/docs/providers/aws/r/s3_bucket.html#kms_master_key_id\n# https://www.terraform.io/docs/providers/aws/r/s3_bucket.html#sse_algorithm\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\n# PASS: A KMS key is provided and sse_algorithm is aws:kms\nresource \"aws_s3_bucket\" \"server_side_encryption_configuration_set\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = aws_kms_key.test_key.arn\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# WARN: No KMS key is specified\nresource \"aws_s3_bucket\" \"server_side_encryption_configuration_set_no_key\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        sse_algorithm = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# WARN: No KMS key is specified\nresource \"aws_s3_bucket\" \"server_side_encryption_configuration_set_no_key_aes256\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        sse_algorithm = \"AES256\"\n      }\n    }\n  }\n}\n\n# FAIL: No server_side_encryption_configuration provided\n# WARN: kms_master_key_id and sse_algorithm not set\nresource \"aws_s3_bucket\" \"server_side_encryption_configuration_not_set\" {\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket/server_side_encryption_enabled/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_ENCRYPTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform12\"\n  -\n    ruleId: S3_BUCKET_KMS\n    warnings: 3\n    failures: 0\n    tags:\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_object/encryption_enabled/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_OBJECT_ENCRYPTION\n    message: S3 Bucket Object should be encrypted\n    resource: aws_s3_bucket_object\n    severity: FAILURE\n    assertions:\n      - or:\n        - key: kms_key_id\n          op: present\n        - key: server_side_encryption\n          op: present\n    tags:\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_object/encryption_enabled/tests/terraform11/encryption_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_kms_key_id\" {\n  key        = \"foo\"\n  bucket     = \"${aws_s3_bucket.test_bucket.id}\"\n  kms_key_id = \"${aws_kms_key.test_key.arn}\"\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_server_side_encryption_s3_default_master_key\" {\n  key                    = \"foo\"\n  bucket                 = \"${aws_s3_bucket.test_bucket.id}\"\n  server_side_encryption = \"aws:kms\"\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_server_side_encryption_aws_managed_key\" {\n  key                    = \"foo\"\n  bucket                 = \"${aws_s3_bucket.test_bucket.id}\"\n  server_side_encryption = \"AES256\"\n}\n\n# Fail\nresource \"aws_s3_bucket_object\" \"encryption_method_not_set\" {\n  key    = \"foo\"\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_object/encryption_enabled/tests/terraform12/encryption_enabled.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  acl = \"private\"\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_kms_key_id\" {\n  key        = \"foo\"\n  bucket     = aws_s3_bucket.test_bucket.id\n  kms_key_id = aws_kms_key.test_key.arn\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_server_side_encryption_s3_default_master_key\" {\n  key                    = \"foo\"\n  bucket                 = aws_s3_bucket.test_bucket.id\n  server_side_encryption = \"aws:kms\"\n}\n\n# Pass\nresource \"aws_s3_bucket_object\" \"encrypt_with_server_side_encryption_aws_managed_key\" {\n  key                    = \"foo\"\n  bucket                 = aws_s3_bucket.test_bucket.id\n  server_side_encryption = \"AES256\"\n}\n\n# Fail\nresource \"aws_s3_bucket_object\" \"encryption_method_not_set\" {\n  key    = \"foo\"\n  bucket = aws_s3_bucket.test_bucket.id\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_object/encryption_enabled/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_OBJECT_ENCRYPTION \n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_action_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard Action in an Allow S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Action\n              op: contains\n              value: \"*\"\n    tags:\n      - policy\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_action_wildcard/tests/terraform11/policy_statement_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_action_without_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:Get*\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:Get*\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_action_wildcard/tests/terraform12/policy_statement_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_action_without_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:Get*\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:Get*\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_action_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_POLICY_WILDCARD_ACTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_NOT_ACTION\n    message: Should not use NotAction in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - policy\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notaction/tests/terraform11/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_without_notaction\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_with_notaction\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notaction/tests/terraform12/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_without_notaction\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_with_notaction\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"s3:GetObject\",\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notprincipal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotPrincipal\n              op: present\n    tags:\n      - policy\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notprincipal/tests/terraform11/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_without_notprincipal\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_with_notprincipal\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notprincipal/tests/terraform12/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_without_notprincipal\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_with_notprincipal\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_notprincipal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_NOT_PRINCIPAL\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_principal_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard Principal in an Allow S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - policy\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_principal_wildcard/tests/terraform11/policy_statement_principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_principal_wildcard/tests/terraform12/policy_statement_principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:s3:::fooBucket/*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_principal_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_secure_transport/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: S3_BUCKET_POLICY_ONLY_HTTPS\n    message: Should only allow HTTPS access to a bucket.\n    resource: aws_s3_bucket_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Condition.Bool.\"aws:SecureTransport\"\n              op: is-false\n    tags:\n      - policy\n      - s3\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_secure_transport/tests/terraform11/policy_statement_secure_transport.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_condition_SecureTransport_set_to_true\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"true\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_condition_SecureTransport_set_to_true\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"true\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_condition_SecureTransport_set_to_false\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"false\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_condition_SecureTransport_set_to_false\" {\n  bucket = \"${aws_s3_bucket.test_bucket.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"false\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_secure_transport/tests/terraform12/policy_statement_secure_transport.tf",
    "content": "## Setup Helper\nresource \"aws_s3_bucket\" \"test_bucket\" {\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_condition_SecureTransport_set_to_true\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"true\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_condition_SecureTransport_set_to_true\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"true\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"policy_statement_deny_condition_SecureTransport_set_to_false\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"false\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_s3_bucket_policy\" \"policy_statement_allow_condition_SecureTransport_set_to_false\" {\n  bucket = aws_s3_bucket.test_bucket.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"s3:GetObject\",\n      \"Resource\": [\n        \"arn:aws:s3:::fooBucket/*\"\n      ],\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"false\"\n        }\n      },\n      \"Principal\": \"*\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/s3/s3_bucket_policy/policy_statement_secure_transport/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: S3_BUCKET_POLICY_ONLY_HTTPS\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_endpoint_configuration/kms_key/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SAGEMAKER_ENDPOINT_ENCRYPTION\n    message: Sagemaker configuration should be encrypted\n    resource: aws_sagemaker_endpoint_configuration\n    severity: WARNING\n    assertions:\n      - key: kms_key_arn\n        op: present\n    tags:\n      - sagemaker\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_endpoint_configuration/kms_key/tests/terraform11/kms_key.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_sagemaker_model\" \"test_model\" {\n  execution_role_arn = \"${aws_iam_role.test_role.arn}\"\n\n  primary_container {\n    image = \"1234567890.dkr.ecr.us-east-1.amazonaws.com/foo:1\"\n  }\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = \"${data.aws_iam_policy_document.test_assume_role.json}\"\n}\n\ndata \"aws_iam_policy_document\" \"test_assume_role\" {\n  statement {\n    actions = [\"sts:AssumeRole\"]\n\n    principals {\n      type        = \"Service\"\n      identifiers = [\"sagemaker.amazonaws.com\"]\n    }\n  }\n}\n\n# Pass\nresource \"aws_sagemaker_endpoint_configuration\" \"kms_key_arn_is_set\" {\n  kms_key_arn = \"${aws_kms_key.test_key.arn}\"\n\n  production_variants {\n    model_name             = \"${aws_sagemaker_model.test_model.name}\"\n    initial_instance_count = 1\n    instance_type          = \"ml.t2.medium\"\n  }\n}\n\n# Warn\nresource \"aws_sagemaker_endpoint_configuration\" \"kms_key_arn_is_not_set\" {\n  production_variants {\n    model_name             = \"${aws_sagemaker_model.test_model.name}\"\n    initial_instance_count = 1\n    instance_type          = \"ml.t2.medium\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_endpoint_configuration/kms_key/tests/terraform12/kms_key.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_sagemaker_model\" \"test_model\" {\n  execution_role_arn = aws_iam_role.test_role.arn\n\n  primary_container {\n    image = \"1234567890.dkr.ecr.us-east-1.amazonaws.com/foo:1\"\n  }\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = data.aws_iam_policy_document.test_assume_role.json\n}\n\ndata \"aws_iam_policy_document\" \"test_assume_role\" {\n  statement {\n    actions = [\"sts:AssumeRole\"]\n\n    principals {\n      type        = \"Service\"\n      identifiers = [\"sagemaker.amazonaws.com\"]\n    }\n  }\n}\n\n# Pass\nresource \"aws_sagemaker_endpoint_configuration\" \"kms_key_arn_is_set\" {\n  kms_key_arn = aws_kms_key.test_key.arn\n\n  production_variants {\n    model_name             = aws_sagemaker_model.test_model.name\n    initial_instance_count = 1\n    instance_type          = \"ml.t2.medium\"\n  }\n}\n\n# Warn\nresource \"aws_sagemaker_endpoint_configuration\" \"kms_key_arn_is_not_set\" {\n  production_variants {\n    model_name             = aws_sagemaker_model.test_model.name\n    initial_instance_count = 1\n    instance_type          = \"ml.t2.medium\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_endpoint_configuration/kms_key/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SAGEMAKER_ENDPOINT_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_notebook_instance/kms_key/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SAGEMAKER_NOTEBOOK_ENCRYPTION\n    message: Sagemaker Notebook should be encrypted\n    resource: aws_sagemaker_notebook_instance\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - sagemaker\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_notebook_instance/kms_key/tests/terraform11/kms_key.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = \"${data.aws_iam_policy_document.test_assume_role.json}\"\n}\n\ndata \"aws_iam_policy_document\" \"test_assume_role\" {\n  statement {\n    actions = [\"sts:AssumeRole\"]\n\n    principals {\n      type        = \"Service\"\n      identifiers = [\"sagemaker.amazonaws.com\"]\n    }\n  }\n}\n\n# Pass\nresource \"aws_sagemaker_notebook_instance\" \"kms_key_id_is_set\" {\n  name          = \"foo\"\n  role_arn      = \"${aws_iam_role.test_role.arn}\"\n  instance_type = \"ml.t2.medium\"\n  kms_key_id    = \"${aws_kms_key.test_key.id}\"\n}\n\n# Warn\nresource \"aws_sagemaker_notebook_instance\" \"kms_key_id_is_not_set\" {\n  name          = \"foo\"\n  role_arn      = \"${aws_iam_role.test_role.arn}\"\n  instance_type = \"ml.t2.medium\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_notebook_instance/kms_key/tests/terraform12/kms_key.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_iam_role\" \"test_role\" {\n  assume_role_policy = data.aws_iam_policy_document.test_assume_role.json\n}\n\ndata \"aws_iam_policy_document\" \"test_assume_role\" {\n  statement {\n    actions = [\"sts:AssumeRole\"]\n\n    principals {\n      type        = \"Service\"\n      identifiers = [\"sagemaker.amazonaws.com\"]\n    }\n  }\n}\n\n# Pass\nresource \"aws_sagemaker_notebook_instance\" \"kms_key_id_is_set\" {\n  name          = \"foo\"\n  role_arn      = aws_iam_role.test_role.arn\n  instance_type = \"ml.t2.medium\"\n  kms_key_id    = aws_kms_key.test_key.id\n}\n\n# Warn\nresource \"aws_sagemaker_notebook_instance\" \"kms_key_id_is_not_set\" {\n  name          = \"foo\"\n  role_arn      = aws_iam_role.test_role.arn\n  instance_type = \"ml.t2.medium\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sagemaker/sagemaker_notebook_instance/kms_key/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SAGEMAKER_NOTEBOOK_ENCRYPTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/ses/ses_identity_policy/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SES_IDENTITY_WILDCARD_PRINCIPAL\n    message: SES identity allow policy should not use a wildcard princpal\n    resource: aws_ses_identity_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - ses\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/ses/ses_identity_policy/wildcard_principal/tests/terraform12/wildcard_principal.tf",
    "content": "# Test that SES identity policy is not using a wildcard principal for allow statements\n# https://www.terraform.io/docs/providers/aws/r/ses_identity_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: SES identity allow without using a wildcard principal\nresource \"aws_ses_identity_policy\" \"ses_allow_without_wildcard\" {\n  identity = \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n  name     = \"example\"\n  policy   = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"ses:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny without using a wildcard principal\nresource \"aws_ses_identity_policy\" \"ses_deny_without_wildcard\" {\n  identity = \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n  name     = \"example\"\n  policy   = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"ses:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Deny using a wildcard principal\nresource \"aws_ses_identity_policy\" \"ses_deny_with_wildcard\" {\n  identity = \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n  name     = \"example\"\n  policy   = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"ses:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: Allow using a wildcard principal\nresource \"aws_ses_identity_policy\" \"ses_allow_with_wildcard\" {\n  identity = \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n  name     = \"example\"\n  policy   = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"ses:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: Allow where principal is a wildcard\nresource \"aws_ses_identity_policy\" \"ses_allow_principal_is_wildcard\" {\n  identity = \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n  name     = \"example\"\n  policy   = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"ses:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:ses:us-west-2:123456789012:identity/example.com\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/ses/ses_identity_policy/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SES_IDENTITY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/shared/exists.todo.txt",
    "content": ""
  },
  {
    "path": "cli/assets/terraform/aws/shared/https.todo.txt",
    "content": ""
  },
  {
    "path": "cli/assets/terraform/aws/shared/kms_key.todo.txt",
    "content": "aws/sagemaker/sagemaker_endpoint_configuration/kms_key\naws/sagemaker/sagemaker_notebook_instance/kms_key\n\n"
  },
  {
    "path": "cli/assets/terraform/aws/shared/policy.todo.txt",
    "content": "│   ├── policy_action_wildcard\n│   ├── policy_notaction\n│   ├── policy_notresource\n│   ├── policy_resource_wildcard\n"
  },
  {
    "path": "cli/assets/terraform/aws/shared/policy_version.todo.txt",
    "content": ""
  },
  {
    "path": "cli/assets/terraform/aws/shared/require_ssl.todo.txt",
    "content": ""
  },
  {
    "path": "cli/assets/terraform/aws/sns/shared/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SNS_POLICY_WILDCARD_PRINCIPAL\n    message: SNS topic allow policy should not use a wildcard princpal\n    resources:\n      - aws_sns_topic\n      - aws_sns_topic_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - sns\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/sns/shared/wildcard_principal/tests/terraform12/sns_topic_policy_wildcard_principal.tf",
    "content": "# Test that SNS topic policy does not use a wildcard principal for allow statements\n# https://www.terraform.io/docs/providers/aws/r/sns_topic_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: SNS topic allow policy does not use a wildcard principal\nresource \"aws_sns_topic_policy\" \"sns_policy_allow_no_wildcard\" {\n  arn    = \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SNS topic deny policy does not use a wildcard principal\nresource \"aws_sns_topic_policy\" \"sns_policy_deny_no_wildcard\" {\n  arn    = \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SNS topic deny policy uses a wildcard principal\nresource \"aws_sns_topic_policy\" \"sns_policy_deny_with_wildcard\" {\n  arn    = \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SNS topic allow policy uses a wildcard principal\nresource \"aws_sns_topic_policy\" \"sns_policy_allow_with_wildcard\" {\n  arn    = \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SNS topic allow policy uses a wildcard principal\nresource \"aws_sns_topic_policy\" \"sns_policy_allow_principal_is_wildcard\" {\n  arn    = \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/shared/wildcard_principal/tests/terraform12/sns_topic_wildcard_principal.tf",
    "content": "# Test that SNS topic policy does not use a wildcard principal for allow statements\n# https://www.terraform.io/docs/providers/aws/r/sns_topic.html#policy\n\n# PASS: SNS topic allow policy does not use a wildcard principal\nresource \"aws_sns_topic\" \"sns_allow_no_wildcard\" {\n  name   = \"test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SNS topic deny policy does not use a wildcard principal\nresource \"aws_sns_topic\" \"sns_deny_no_wildcard\" {\n  name   = \"test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SNS topic deny policy uses a wildcard principal\nresource \"aws_sns_topic\" \"sns_deny_with_wildcard\" {\n  name   = \"test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SNS topic allow policy uses a wildcard principal\nresource \"aws_sns_topic\" \"sns_allow_with_wildcard\" {\n  name   = \"test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SNS topic allow policy uses a wildcard principal\nresource \"aws_sns_topic\" \"sns_allow_principal_is_wildcard\" {\n  name   = \"test-topic\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sns:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sns:us-east-1:123456789012:test-topic\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/shared/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SNS_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SNS_TOPIC_POLICY_NOT_ACTION\n    message: Should not use NotAction in SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - sns\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notaction/tests/terraform11/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notaction\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notaction\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notaction/tests/terraform12/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notaction\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notaction\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SNS_TOPIC_POLICY_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notprincipal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SNS_TOPIC_POLICY_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotPrincipal\n              op: present\n    tags:\n      - sns\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notprincipal/tests/terraform11/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notprincipal\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sns_topic_policy\" \"policy_statement_with_notprincipal\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notprincipal/tests/terraform12/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_without_notprincipal\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sns_topic_policy\" \"policy_statement_with_notprincipal\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_notprincipal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SNS_TOPIC_POLICY_NOT_PRINCIPAL\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_principal_wildcard-copy/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SNS_TOPIC_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard Principal in an Allow SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - sns\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_principal_wildcard-copy/tests/terraform11/policy_statement_principal_wildcard-copy.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sns_topic_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_principal_wildcard-copy/tests/terraform12/policy_statement_principal_wildcard-copy.tf",
    "content": "## Setup Helper\nresource \"aws_sns_topic\" \"test_topic\" {\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sns_topic_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  arn = \"${aws_sns_topic.test_topic.arn}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sns_topic_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  arn = aws_sns_topic.test_topic.arn\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sns:Publish\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo*\"\n        ]\n      },\n      \"Resource\": \"arn:aws:sns:us-east-1:1234567890:fooTopic\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sns/sns_topic_policy/topic_policy_statement_principal_wildcard-copy/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SNS_TOPIC_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/shared/wildcard_principal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_POLICY_WILDCARD_PRINCIPAL\n    message: SQS queue allow policy should not use a wildcard princpal\n    resources:\n      - aws_sqs_queue\n      - aws_sqs_queue_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - sqs\n      - policy"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/shared/wildcard_principal/tests/terraform12/sqs_queue_policy_wildcard_principal.tf",
    "content": "# Test that SQS queue policy does not use a wildcard principal for allow statements\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue_policy.html#policy\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# PASS: SQS queue allow policy does not use a wildcard principal\nresource \"aws_sqs_queue_policy\" \"sqs_policy_allow_no_wildcard\" {\n  queue_url = \"https://queue.amazonaws.com/0123456789012/myqueue\"\n  policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SQS queue deny policy does not use a wildcard principal\nresource \"aws_sqs_queue_policy\" \"sqs_policy_deny_no_wildcard\" {\n  queue_url = \"https://queue.amazonaws.com/0123456789012/myqueue\"\n  policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SQS queue deny policy uses a wildcard principal\nresource \"aws_sqs_queue_policy\" \"sqs_policy_deny_with_wildcard\" {\n  queue_url = \"https://queue.amazonaws.com/0123456789012/myqueue\"\n  policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SQS queue allow policy uses a wildcard principal\nresource \"aws_sqs_queue_policy\" \"sqs_policy_allow_with_wildcard\" {\n  queue_url = \"https://queue.amazonaws.com/0123456789012/myqueue\"\n  policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SQS queue allow policy uses a wildcard principal\nresource \"aws_sqs_queue_policy\" \"sqs_policy_allow_principal_is_wildcard\" {\n  queue_url = \"https://queue.amazonaws.com/0123456789012/myqueue\"\n  policy    = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/shared/wildcard_principal/tests/terraform12/sqs_queue_wildcard_principal.tf",
    "content": "# Test that SQS queue policy does not use a wildcard principal for allow statements\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue.html#policy\n\n# PASS: SQS queue allow policy does not use a wildcard principal\nresource \"aws_sqs_queue\" \"sqs_policyallow_no_wildcard\" {\n  name   = \"test-queue\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SQS queue deny policy does not use a wildcard principal\nresource \"aws_sqs_queue\" \"sqs_policydeny_no_wildcard\" {\n  name   = \"test-queue\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/foo\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# PASS: SQS queue deny policy uses a wildcard principal\nresource \"aws_sqs_queue\" \"sqs_policydeny_with_wildcard\" {\n  name   = \"test-queue\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Deny\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SQS queue allow policy uses a wildcard principal\nresource \"aws_sqs_queue\" \"sqs_policyallow_with_wildcard\" {\n  name   = \"test-queue\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"arn:aws:iam::1234567890:user/*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n\n# FAIL: SQS queue allow policy uses a wildcard principal\nresource \"aws_sqs_queue\" \"sqs_policyallow_principal_is_wildcard\" {\n  name   = \"test-queue\"\n  policy = <<EOF\n{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Action\": \"sqs:*\",\n            \"Principal\": {\n                \"AWS\": [\n                    \"*\"\n                ]\n            },\n            \"Effect\": \"Allow\",\n            \"Resource\": \"arn:aws:sqs:us-east-2:444455556666:queue1\"\n        }\n    ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/shared/wildcard_principal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 4\n    tags:\n      - \"terraform12\""
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue/encryption/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_QUEUE_ENCRYPTION\n    message: SQSQueue should use encryption and define a reuse period\n    resource: aws_sqs_queue\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: kms_master_key_id, kms_data_key_reuse_period_seconds\n    tags:\n      - sqs\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue/encryption/tests/terraform11/encryption.tf",
    "content": "# Test that sqs queue has kms_master_key_id and kms_data_key_reuse_period_seconds defined\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue.html#kms_master_key_id\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue.html#kms_data_key_reuse_period_seconds\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# Pass: both kms_master_key_id and kms_data_key_reuse_period_seconds are used\nresource \"aws_sqs_queue\" \"encryption_enabled_with_reuse_set\" {\n  name                              = \"queue-with-encryption\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Fail: neither kms_master_key_id or kms_data_key_reuse_period_seconds are used\nresource \"aws_sqs_queue\" \"encryption_disabled\" {\n  name = \"queue-without-encryption\"\n}\n\n# Fail: kms key defined without kms_data_key_reuse_period_seconds\nresource \"aws_sqs_queue\" \"encryption_without_reuse_period\" {\n  name              = \"queue-without-encryption\"\n  kms_master_key_id = \"alias/foo/bar\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue/encryption/tests/terraform12/encryption.tf",
    "content": "# Test that sqs queue has kms_master_key_id and kms_data_key_reuse_period_seconds defined\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue.html#kms_master_key_id\n# https://www.terraform.io/docs/providers/aws/r/sqs_queue.html#kms_data_key_reuse_period_seconds\n\nprovider \"aws\" {\n  region = \"us-east-1\"\n}\n\n# Pass: both kms_master_key_id and kms_data_key_reuse_period_seconds are used\nresource \"aws_sqs_queue\" \"encryption_enabled_with_reuse_set\" {\n  name                              = \"queue-with-encryption\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Fail: neither kms_master_key_id or kms_data_key_reuse_period_seconds are used\nresource \"aws_sqs_queue\" \"encryption_disabled\" {\n  name = \"queue-without-encryption\"\n}\n\n# Fail: kms key defined without kms_data_key_reuse_period_seconds\nresource \"aws_sqs_queue\" \"encryption_without_reuse_period\" {\n  name              = \"queue-without-encryption\"\n  kms_master_key_id = \"alias/foo/bar\"\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue/encryption/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_QUEUE_ENCRYPTION\n    warnings: 0\n    failures: 3\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_action_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_QUEUE_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard action in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Action\n              op: contains\n              value: \"*\"\n    tags:\n      - sqs\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_action_wildcard/tests/terraform11/policy_statement_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_action_without_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_action_wildcard/tests/terraform12/policy_statement_action_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n  arn                               = \"mockedarn\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_action_without_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nPOLICY\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_action_without_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nPOLICY\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_action_with_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nPOLICY\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_action_with_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nPOLICY\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_action_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_QUEUE_POLICY_WILDCARD_ACTION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notaction/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_QUEUE_POLICY_NOT_ACTION\n    message: Should not use NotAction in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotAction\n              op: present\n    tags:\n      - sqs\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notaction/tests/terraform11/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_without_notaction\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sqs_queue_policy\" \"policy_statement_with_notaction\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notaction/tests/terraform12/policy_statement_notaction.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n  arn                               = \"mocked_arn\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_without_notaction\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sqs_queue_policy\" \"policy_statement_with_notaction\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotAction\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notaction/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_QUEUE_POLICY_NOT_ACTION\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notprincipal/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_QUEUE_POLICY_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: NotPrincipal\n              op: present\n    tags:\n      - sqs\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notprincipal/tests/terraform11/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_without_notprincipal\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sqs_queue_policy\" \"policy_statement_with_notprincipal\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notprincipal/tests/terraform12/policy_statement_notprincipal.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n  arn                               = \"mocked_arn\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_without_notprincipal\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Warn\nresource \"aws_sqs_queue_policy\" \"policy_statement_with_notprincipal\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"NotPrincipal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_notprincipal/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_QUEUE_POLICY_NOT_PRINCIPAL\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_principal_wildcard/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SQS_QUEUE_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard Principal in an Allow SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: FAILURE\n    assertions:\n      - none:\n          key: policy.Statement[]\n          expressions:\n            - key: Effect\n              op: eq\n              value: Allow\n            - key: Principal\n              op: contains\n              value: \"*\"\n    tags:\n      - sqs\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_principal_wildcard/tests/terraform11/policy_statement_principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": \"*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": \"*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_principal_wildcard/tests/terraform12/policy_statement_principal_wildcard.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name                              = \"test_queue\"\n  kms_master_key_id                 = \"alias/foo/bar\"\n  kms_data_key_reuse_period_seconds = 60\n  arn                               = \"mocked_arn\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_principal_without_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_principal_without_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": {\n        \"AWS\": [\n          \"arn:aws:iam::1234567890:user/foo\"\n        ]\n      },\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_statement_deny_principal_with_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Deny\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": \"*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_statement_allow_principal_with_wildcard\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Principal\": \"*\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_statement_principal_wildcard/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SQS_QUEUE_POLICY_WILDCARD_PRINCIPAL\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_version/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: POLICY_VERSION\n    message: Version in IAM Policy should be 2012-10-17\n    resources:\n      - aws_sns_topic_policy\n      - aws_s3_bucket_policy\n      - aws_sqs_queue_policy\n      - aws_iam_policy\n      - aws_iam_role_policy\n    severity: FAILURE\n    assertions:\n      - key: policy.Version\n        op: eq\n        value: \"2012-10-17\"\n    tags:\n      - iam\n      - policy\n  - id: ASSUME_ROLEPOLICY_VERSION\n    message: Version in IAM Policy should be 2012-10-17\n    resource: aws_iam_role\n    severity: FAILURE\n    assertions:\n      - key: assume_role_policy.Version\n        op: eq\n        value: \"2012-10-17\"\n    tags:\n      - iam\n      - policy\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_version/tests/terraform11/policy_version.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name = \"examplequeue\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_version_set_correctly\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_version_set_incorrectly\" {\n  queue_url = \"${aws_sqs_queue.test_queue.id}\"\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_version/tests/terraform12/policy_version.tf",
    "content": "## Setup Helper\nresource \"aws_sqs_queue\" \"test_queue\" {\n  name = \"examplequeue\"\n  arn = \"mocked_arn\"\n}\n\n# Pass\nresource \"aws_sqs_queue_policy\" \"policy_version_set_correctly\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n\n# Fail\nresource \"aws_sqs_queue_policy\" \"policy_version_set_incorrectly\" {\n  queue_url = aws_sqs_queue.test_queue.id\n\n  policy = <<EOF\n{\n  \"Version\": \"2008-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.test_queue.arn}\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/sqs/sqs_queue_policy/policy_version/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: POLICY_VERSION\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_all_protocols/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_EGRESS_ALL_PROTOCOLS\n    resource: aws_security_group\n    message: Best practices recommend not opening all protocols and ports to egress traffic\n    severity: WARNING\n    assertions:\n      - not:\n        - key: \"egress[].protocol\"\n          op: contains\n          value: \"-1\"\n    tags:\n      - sg\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_all_protocols/tests/terraform11/egress_all_protocols.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_tcp_protocol\" {\n  name        = \"allow_tcp\"\n  description = \"Allow TCP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_udp_protocol\" {\n  name        = \"allow_udp\"\n  description = \"Allow UDP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"udp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_icmp_protocol\" {\n  name        = \"allow_icmp\"\n  description = \"Allow ICMP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"icmp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_all_protocols\" {\n  name        = \"allow_all\"\n  description = \"Allow ALL traffic\"\n  egress {\n    from_port = 0\n    to_port   = 0\n    protocol  = \"-1\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_all_protocols/tests/terraform12/egress_all_protocols.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_tcp_protocol\" {\n  name        = \"allow_tcp\"\n  description = \"Allow TCP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_udp_protocol\" {\n  name        = \"allow_udp\"\n  description = \"Allow UDP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"udp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_icmp_protocol\" {\n  name        = \"allow_icmp\"\n  description = \"Allow ICMP traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"icmp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_all_protocols\" {\n  name        = \"allow_all\"\n  description = \"Allow ALL traffic\"\n  egress {\n    from_port = 0\n    to_port   = 0\n    protocol  = \"-1\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_all_protocols/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_EGRESS_ALL_PROTOCOLS\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_port_range/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_EGRESS_PORT_RANGE\n    resource: aws_security_group\n    message: Security group egress should specify single port instead of range\n    severity: WARNING\n    assertions:\n      - key: \"egress[?(from_port!=to_port)]\"\n        op: empty\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_port_range/tests/terraform11/egress_port_range.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_port_range_matches\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_port_range_does_not_match\" {\n  name        = \"allow_foo\"\n  description = \"Allow FOO traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10200\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_port_range/tests/terraform12/egress_port_range.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_port_range_matches\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_port_range_does_not_match\" {\n  name        = \"allow_foo\"\n  description = \"Allow FOO traffic\"\n  egress {\n    from_port = 10000\n    to_port   = 10200\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/egress_port_range/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_EGRESS_PORT_RANGE\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_all_protocols/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_INGRESS_ALL_PROTOCOLS\n    resource: aws_security_group\n    message: Best practices recommend not opening all protocols and ports to ingress traffic\n    severity: WARNING\n    assertions:\n      - not:\n        - key: \"ingress[].protocol\"\n          op: contains\n          value: \"-1\"\n    tags:\n      - sg\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_all_protocols/tests/terraform11/ingress_all_protocols.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_tcp_protocol\" {\n  name        = \"allow_tcp\"\n  description = \"Allow TCP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_udp_protocol\" {\n  name        = \"allow_udp\"\n  description = \"Allow UDP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"udp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_icmp_protocol\" {\n  name        = \"allow_icmp\"\n  description = \"Allow ICMP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"icmp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_all_protocols\" {\n  name        = \"allow_all\"\n  description = \"Allow ALL traffic\"\n  ingress {\n    from_port = 0\n    to_port   = 0\n    protocol  = \"-1\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_all_protocols/tests/terraform12/ingress_all_protocols.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_tcp_protocol\" {\n  name        = \"allow_tcp\"\n  description = \"Allow TCP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_udp_protocol\" {\n  name        = \"allow_udp\"\n  description = \"Allow UDP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"udp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_icmp_protocol\" {\n  name        = \"allow_icmp\"\n  description = \"Allow ICMP traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10000\n    protocol  = \"icmp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_all_protocols\" {\n  name        = \"allow_all\"\n  description = \"Allow ALL traffic\"\n  ingress {\n    from_port = 0\n    to_port   = 0\n    protocol  = \"-1\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_all_protocols/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_INGRESS_ALL_PROTOCOLS\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_port_range/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_INGRESS_PORT_RANGE\n    resource: aws_security_group\n    message: Security group ingress should specify single port instead of range\n    severity: WARNING\n    assertions:\n      - key: \"ingress[?(from_port!=to_port)]\"\n        op: empty\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_port_range/tests/terraform11/ingress_port_range.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_port_range_matches\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_port_range_does_not_match\" {\n  name        = \"allow_foo\"\n  description = \"Allow FOO traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10200\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_port_range/tests/terraform12/ingress_port_range.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_port_range_matches\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_port_range_does_not_match\" {\n  name        = \"allow_foo\"\n  description = \"Allow FOO traffic\"\n  ingress {\n    from_port = 10000\n    to_port   = 10200\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ingress_port_range/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_INGRESS_PORT_RANGE\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/missing_egress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_MISSING_EGRESS\n    resource: aws_security_group\n    message: Security group should specify egress rules\n    severity: WARNING\n    assertions:\n      - key: \"egress\"\n        op: present\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/missing_egress/tests/terraform11/missing_egress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_block_exists\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_block_missing\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/missing_egress/tests/terraform12/missing_egress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_block_exists\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_block_missing\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/missing_egress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_MISSING_EGRESS\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/non_32_ingress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_NON_32_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from CIDR block that does not end in /32\n    severity: WARNING\n    assertions:\n      - every:\n          key: \"ingress[].cidr_blocks[]\"\n          expressions:\n            - key: \"@\"\n              op: regex\n              value: .*/32$\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/non_32_ingress/tests/terraform11/non_32_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_32_subnet\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_24_subnet\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.0/24\"]\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_multiple_subnets\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n    cidr_blocks = [\n      \"10.0.0.100/32\",\n      \"10.0.0.110/32\",\n      \"10.0.0.120/32\",\n      \"10.0.0.130/32\",\n      \"10.1.0.0/24\"\n    ]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/non_32_ingress/tests/terraform12/non_32_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_32_subnet\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_24_subnet\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.0/24\"]\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_with_multiple_subnets\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n    cidr_blocks = [\n      \"10.0.0.100/32\",\n      \"10.0.0.110/32\",\n      \"10.0.0.120/32\",\n      \"10.0.0.130/32\",\n      \"10.1.0.0/24\"\n    ]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/non_32_ingress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_NON_32_INGRESS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/rdp_world_ingress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_RDP_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow RDP ingress from 0.0.0.0/0 or ::/0\n    severity: FAILURE\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[?(from_port=='3389')].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port=='3389')].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n      - not:\n        - or:\n          - key: \"ingress[?(from_port==`3389`)].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port==`3389`)].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/rdp_world_ingress/tests/terraform11/rdp_world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port = 3389\n    to_port   = 3389\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port   = 3389\n    to_port     = 3389\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port   = 3389\n    to_port     = 3389\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port        = 3389\n    to_port          = 3389\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port        = 3389\n    to_port          = 3389\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/rdp_world_ingress/tests/terraform12/rdp_world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port = 3389\n    to_port   = 3389\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port   = 3389\n    to_port     = 3389\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port   = 3389\n    to_port     = 3389\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_rdp_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port        = 3389\n    to_port          = 3389\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_rdp_is_set\" {\n  name        = \"allow_rdp\"\n  description = \"Allow RDP traffic\"\n  ingress {\n    from_port        = 3389\n    to_port          = 3389\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/rdp_world_ingress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_RDP_WORLD_INGRESS\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ssh_world_ingress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_SSH_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow SSH ingress from 0.0.0.0/0 or ::/0\n    severity: FAILURE\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[?(from_port=='22')].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port=='22')].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n      - not:\n        - or:\n          - key: \"ingress[?(from_port==`22`)].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port==`22`)].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ssh_world_ingress/tests/terraform11/ssh_world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port = 22\n    to_port   = 22\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port        = 22\n    to_port          = 22\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port        = 22\n    to_port          = 22\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ssh_world_ingress/tests/terraform12/ssh_world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port = 22\n    to_port   = 22\n    protocol  = \"tcp\"\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_ssh_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port        = 22\n    to_port          = 22\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n\n# Fail\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world_and_ssh_is_set\" {\n  name        = \"allow_ssh\"\n  description = \"Allow SSH traffic\"\n  ingress {\n    from_port        = 22\n    to_port          = 22\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/ssh_world_ingress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_SSH_WORLD_INGRESS\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_egress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_WORLD_EGRESS\n    resource: aws_security_group\n    message: Security group should not allow egress to 0.0.0.0/0 or ::/0\n    severity: WARNING\n    assertions:\n      - not:\n        - or:\n          - key: \"egress[].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"egress[].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_egress/tests/terraform11/world_egress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_egress/tests/terraform12/world_egress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"egress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"egress_ipv6_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  egress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_egress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_WORLD_EGRESS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_ingress/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: SG_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from 0.0.0.0/0 or ::/0\n    severity: WARNING\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[].ipv6_cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n      - ec2\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_ingress/tests/terraform11/world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_ingress/tests/terraform12/world_ingress.tf",
    "content": "# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"tcp\"\n    cidr_blocks = [\"10.0.0.100/32\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_not_set\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port = 80\n    to_port   = 80\n    protocol  = \"tcp\"\n  }\n}\n\n# Warn\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_world\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"::/0\"]\n  }\n}\n\n# Pass\nresource \"aws_security_group\" \"ingress_ipv6_cidr_blocks_set_to_ip\" {\n  name        = \"allow_http\"\n  description = \"Allow HTTP traffic\"\n  ingress {\n    from_port        = 80\n    to_port          = 80\n    protocol         = \"tcp\"\n    ipv6_cidr_blocks = [\"0:0:0:0:0:ffff:a00:64/32\"]\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/security_group/world_ingress/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: SG_WORLD_INGRESS\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/subnet/map_public_ip_on_launch/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: EC2_SUBNET_MAP_PUBLIC\n    message: EC2 Subnet should not have MapPublicIpOnLaunch set to true\n    resource: aws_subnet\n    severity: WARNING\n    assertions:\n    - not:\n      - key: map_public_ip_on_launch\n        op: is-true\n    tags:\n      - ec2\n      - subnet\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/subnet/map_public_ip_on_launch/tests/terraform11/map_public_ip_on_launch.tf",
    "content": "## Setup Helper\nresource \"aws_vpc\" \"test_vpc\" {\n  cidr_block = \"10.0.0.0/16\"\n}\n\n# Pass\nresource \"aws_subnet\" \"map_public_ip_on_launch_not_set\" {\n  vpc_id     = \"${aws_vpc.test_vpc.id}\"\n  cidr_block = \"172.2.0.0/24\"\n}\n\n# Pass\nresource \"aws_subnet\" \"map_public_ip_on_launch_set_to_false\" {\n  vpc_id                  = \"${aws_vpc.test_vpc.id}\"\n  cidr_block              = \"172.2.0.0/24\"\n  map_public_ip_on_launch = false\n}\n\n# Warn\nresource \"aws_subnet\" \"map_public_ip_on_launch_set_to_true\" {\n  vpc_id                  = \"${aws_vpc.test_vpc.id}\"\n  cidr_block              = \"172.2.0.0/24\"\n  map_public_ip_on_launch = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/subnet/map_public_ip_on_launch/tests/terraform12/map_public_ip_on_launch.tf",
    "content": "## Setup Helper\nresource \"aws_vpc\" \"test_vpc\" {\n  cidr_block = \"10.0.0.0/16\"\n}\n\n# Pass\nresource \"aws_subnet\" \"map_public_ip_on_launch_not_set\" {\n  vpc_id     = aws_vpc.test_vpc.id\n  cidr_block = \"172.2.0.0/24\"\n}\n\n# Pass\nresource \"aws_subnet\" \"map_public_ip_on_launch_set_to_false\" {\n  vpc_id                  = aws_vpc.test_vpc.id\n  cidr_block              = \"172.2.0.0/24\"\n  map_public_ip_on_launch = false\n}\n\n# Warn\nresource \"aws_subnet\" \"map_public_ip_on_launch_set_to_true\" {\n  vpc_id                  = aws_vpc.test_vpc.id\n  cidr_block              = \"172.2.0.0/24\"\n  map_public_ip_on_launch = true\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/vpc/subnet/map_public_ip_on_launch/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: EC2_SUBNET_MAP_PUBLIC\n    warnings: 1\n    failures: 0\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/assets/terraform/aws/waf/waf_web_acl/default_action_type/rule.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n\n  - id: WAF_WEB_ACL\n    message: Default action for WAF should not be ALLOW\n    resource: aws_waf_web_acl\n    severity: FAILURE\n    assertions:\n      - none:\n          key: default_action\n          expressions:\n            - key: type\n              op: eq\n              value: ALLOW\n    tags:\n      - waf\n"
  },
  {
    "path": "cli/assets/terraform/aws/waf/waf_web_acl/default_action_type/tests/terraform11/default_action_type.tf",
    "content": "# Pass\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_block\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"BLOCK\"\n  }\n}\n\n# Pass\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_count\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"BLOCK\"\n  }\n}\n\n# Fail\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_allow\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"ALLOW\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/waf/waf_web_acl/default_action_type/tests/terraform12/default_action_type.tf",
    "content": "# Pass\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_block\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"BLOCK\"\n  }\n}\n\n# Pass\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_count\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"BLOCK\"\n  }\n}\n\n# Fail\nresource \"aws_waf_web_acl\" \"default_action_type_set_to_allow\" {\n  name        = \"foo\"\n  metric_name = \"foo\"\n\n  default_action {\n    type = \"ALLOW\"\n  }\n}\n"
  },
  {
    "path": "cli/assets/terraform/aws/waf/waf_web_acl/default_action_type/tests/test.yml",
    "content": "---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: WAF_WEB_ACL\n    warnings: 0\n    failures: 1\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n"
  },
  {
    "path": "cli/builtin_test.go",
    "content": "package main\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc loadRules(t *testing.T, filename string) assertion.RuleSet {\n\tr, err := loadBuiltInRuleSet(filename)\n\tassert.Nil(t, err, \"Cannot load ruleset file\")\n\treturn r\n}\n\ntype BuiltInTestCase struct {\n\tFilename     string\n\tRuleID       string\n\tWarningCount int\n\tFailureCount int\n}\n\nfunc numberOfWarnings(violations []assertion.Violation) int {\n\tn := 0\n\tfor _, v := range violations {\n\t\tif v.Status == \"WARNING\" {\n\t\t\tn++\n\t\t}\n\t}\n\treturn n\n}\nfunc numberOfFailures(violations []assertion.Violation) int {\n\tn := 0\n\tfor _, v := range violations {\n\t\tif v.Status == \"FAILURE\" {\n\t\t\tn++\n\t\t}\n\t}\n\treturn n\n}\n\n// String build message for violations. Debug helper\nfunc getViolationsString(severity string, violations []assertion.Violation) string {\n\tvar violationsReported string\n\tfor count, v := range violations {\n\t\tif v.Status == severity {\n\t\t\tviolationsReported += strconv.Itoa(count+1) + \". Violation:\"\n\t\t\tviolationsReported += \"\\n\\tRule Message: \" + v.RuleMessage\n\t\t\tviolationsReported += \"\\n\\tRule Id: \" + v.RuleID\n\t\t\tviolationsReported += \"\\n\\tResource ID: \" + v.ResourceID\n\t\t\tviolationsReported += \"\\n\\tResource Type: \" + v.ResourceType\n\t\t\tviolationsReported += \"\\n\\tCategory: \" + v.Category\n\t\t\tviolationsReported += \"\\n\\tStatus: \" + v.Status\n\t\t\tviolationsReported += \"\\n\\tAssertion Message: \" + v.AssertionMessage\n\t\t\tviolationsReported += \"\\n\\tFilename: \" + v.Filename\n\t\t\tviolationsReported += \"\\n\\tLine Number: \" + strconv.Itoa(v.LineNumber)\n\t\t\tviolationsReported += \"\\n\\tCreated At: \" + v.CreatedAt + \"\\n\"\n\t\t}\n\t}\n\treturn violationsReported\n}\n"
  },
  {
    "path": "cli/options.go",
    "content": "package main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\nfunc getCommandLineOptions() CommandLineOptions {\n\n\tcommandLineOptions := CommandLineOptions{}\n\tcommandLineOptions.TerraformBuiltInRules = flag.Bool(\"terraform\", false, \"Use built-in rules for Terraform.\")\n\tflag.Var(&commandLineOptions.RulesFilenames, \"rules\", \"Rules file, can be specified multiple times\")\n\t//flag.Var(&commandLineOptions.Parser, \"parser\", \"Version of Terraform parser to use (either tf12 or tf11\")\n\tcommandLineOptions.TerraformParser = flag.String(\"tfparser\", \"\", \"Version of Terraform parser to use (must be either 'tf12' or 'tf11')\")\n\tcommandLineOptions.Tags = flag.String(\"tags\", \"\", \"Run only tests with tags in this comma separated list\")\n\tcommandLineOptions.Ids = flag.String(\"ids\", \"\", \"Run only the rules in this comma separated list\")\n\tcommandLineOptions.IgnoreIds = flag.String(\"ignore-ids\", \"\", \"Ignore the rules in this comma separated list\")\n\tcommandLineOptions.QueryExpression = flag.String(\"query\", \"\", \"JMESPath expression to query the results\")\n\tcommandLineOptions.VerboseReport = flag.Bool(\"verbose\", false, \"Output a verbose report\")\n\tcommandLineOptions.SearchExpression = flag.String(\"search\", \"\", \"JMESPath expression to evaluation against the files\")\n\tcommandLineOptions.Validate = flag.Bool(\"validate\", false, \"Validate rules file\")\n\tcommandLineOptions.Version = flag.Bool(\"version\", false, \"Get program version\")\n\tcommandLineOptions.ProfileFilename = flag.String(\"profile\", \"\", \"Provide default options\")\n\tflag.Var(&commandLineOptions.ExcludePatterns, \"exclude\", \"Filename patterns to exclude\")\n\tflag.Var(&commandLineOptions.ExcludeFromFilenames, \"exclude-from\", \"Filename containing patterns to exclude\")\n\tflag.Var(&commandLineOptions.Variables, \"var\", \"Variable values for rules with ValueFrom.Variable\")\n\tcommandLineOptions.Debug = flag.Bool(\"debug\", false, \"Debug logging\")\n\n\tflag.Parse()\n\n\tcommandLineOptions.Args = flag.Args()\n\treturn commandLineOptions\n}\n\nfunc getLinterOptions(o CommandLineOptions, p ProfileOptions) (LinterOptions, error) {\n\texcludePatterns := append(o.ExcludePatterns, p.ExcludePatterns...)\n\texcludeFromFilenames := append(o.ExcludeFromFilenames, p.ExcludeFromFilenames...)\n\tallExcludePatterns, err := loadExcludePatterns(excludePatterns, excludeFromFilenames)\n\tif err != nil {\n\t\treturn LinterOptions{}, err\n\t}\n\ttfParser, err := validateParser(*o.TerraformParser)\n\tif err != nil {\n\t\treturn LinterOptions{}, err\n\t}\n\tlinterOptions := LinterOptions{\n\t\tTags:             makeTagList(*o.Tags, p.Tags),\n\t\tRuleIDs:          makeRulesList(*o.Ids, p.IDs),\n\t\tIgnoreRuleIDs:    makeRulesList(*o.IgnoreIds, p.IgnoreIDs),\n\t\tQueryExpression:  makeQueryExpression(*o.QueryExpression, *o.VerboseReport, p.Query),\n\t\tSearchExpression: *o.SearchExpression,\n\t\tExcludePatterns:  allExcludePatterns,\n\t\tVariables:        mergeVariables(p.Variables, parseVariables(o.Variables)),\n\t\tTerraformParser:  tfParser,\n\t}\n\treturn linterOptions, nil\n}\n\nfunc loadProfile(filename string) (ProfileOptions, error) {\n\tdefaultFilename := \"config-lint.yml\"\n\tvar options ProfileOptions\n\tif filename == \"\" {\n\t\tfilename = defaultFilename\n\t}\n\tbb, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tif filename == defaultFilename {\n\t\t\treturn options, nil\n\t\t}\n\t\treturn options, err\n\t}\n\terr = yaml.Unmarshal(bb, &options)\n\tif err != nil {\n\t\treturn options, err\n\t}\n\tif len(options.Files) > 0 {\n\t\tpatterns := options.Files\n\t\toptions.Files = []string{}\n\t\tfor _, pattern := range patterns {\n\t\t\tmatches, err := filepath.Glob(pattern)\n\t\t\tif err != nil {\n\t\t\t\treturn options, err\n\t\t\t}\n\t\t\toptions.Files = append(options.Files, matches...)\n\t\t}\n\t}\n\treturn options, nil\n}\n\nfunc makeTagList(tags string, profileOptions []string) []string {\n\tif tags != \"\" {\n\t\treturn strings.Split(tags, \",\")\n\t}\n\tif len(profileOptions) != 0 {\n\t\treturn profileOptions\n\t}\n\treturn nil\n}\n\nfunc makeRulesList(ruleIDs string, profileOptions []string) []string {\n\tif ruleIDs != \"\" {\n\t\treturn strings.Split(ruleIDs, \",\")\n\t}\n\tif len(profileOptions) != 0 {\n\t\treturn profileOptions\n\t}\n\treturn nil\n}\n\nfunc makeQueryExpression(queryExpression string, verboseReport bool, profileOptions string) string {\n\tif queryExpression != \"\" {\n\t\treturn queryExpression\n\t}\n\t// return complete report when -verbose option is used\n\tif verboseReport {\n\t\treturn \"\"\n\t}\n\tif profileOptions != \"\" {\n\t\treturn profileOptions\n\t}\n\t// default is to only report Violations\n\treturn \"Violations[]\"\n}\n\nfunc parseVariables(vars []string) map[string]string {\n\tm := map[string]string{}\n\tfor _, kv := range vars {\n\t\tparts := strings.Split(kv, \"=\")\n\t\tif len(parts) == 2 {\n\t\t\tm[parts[0]] = parts[1]\n\t\t} else {\n\t\t\tfmt.Printf(\"Cannot parse command line variable: %s\\n\", kv)\n\t\t}\n\t}\n\treturn m\n}\n\nfunc mergeVariables(a, b map[string]string) map[string]string {\n\tif a == nil {\n\t\treturn b\n\t}\n\tif b == nil {\n\t\treturn map[string]string{}\n\t}\n\tfor k, v := range b {\n\t\ta[k] = v\n\t}\n\treturn a\n}\n\nfunc loadExcludePatterns(patterns []string, excludeFromFilenames []string) ([]string, error) {\n\tif len(excludeFromFilenames) == 0 {\n\t\treturn patterns, nil\n\t}\n\tfor _, filename := range excludeFromFilenames {\n\t\tlines, err := ioutil.ReadFile(filename)\n\t\tif err != nil {\n\t\t\treturn patterns, err\n\t\t}\n\t\tfor _, patternFromFile := range strings.Split(string(lines), \"\\n\") {\n\t\t\tif patternFromFile != \"\" {\n\t\t\t\tassertion.Debugf(\"Pattern from file %s: %s\\n\", filename, patternFromFile)\n\t\t\t\tpatterns = append(patterns, patternFromFile)\n\t\t\t}\n\t\t}\n\t}\n\treturn patterns, nil\n}\n\nfunc validateParser(parser string) (string, error) {\n\tvalidOptions := []string{\"\", \"tf11\", \"tf12\"}\n\tfor _, option := range validOptions {\n\t\tif parser == option {\n\t\t\treturn parser, nil\n\t\t}\n\t}\n\treturn \"\", fmt.Errorf(\"tf-parser \\\"%s\\\" is not valid. Choose from [\\\"tf11\\\", \\\"tf12\\\"].\\n\", parser)\n}\n"
  },
  {
    "path": "cli/options_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n)\n\nfunc emptyCommandLineOptions() CommandLineOptions {\n\temptyString := \"\"\n\tverbose := false\n\treturn CommandLineOptions{\n\t\tTags:             &emptyString,\n\t\tIds:              &emptyString,\n\t\tIgnoreIds:        &emptyString,\n\t\tQueryExpression:  &emptyString,\n\t\tSearchExpression: &emptyString,\n\t\tVerboseReport:    &verbose,\n\t\tTerraformParser:  &emptyString,\n\t}\n}\n\nfunc TestCommandLineOnlyOptions(t *testing.T) {\n\ttags := \"1,2,3\"\n\to := emptyCommandLineOptions()\n\to.Tags = &tags\n\tp := ProfileOptions{}\n\tl, err := getLinterOptions(o, p)\n\n\tif err != nil {\n\t\tt.Errorf(\"getLinterOptions should not return error: %s\\n\", err.Error())\n\t}\n\tif len(l.Tags) != 3 {\n\t\tt.Errorf(\"getLinterOptions should find 3 tags: %v\\n\", l.Tags)\n\t}\n}\n\nfunc TestProfileOnlyOptions(t *testing.T) {\n\to := emptyCommandLineOptions()\n\tp := ProfileOptions{\n\t\tTags: []string{\"1\", \"2\", \"3\"},\n\t}\n\tl, err := getLinterOptions(o, p)\n\n\tif err != nil {\n\t\tt.Errorf(\"getLinterOptions should not return error: %s\\n\", err.Error())\n\t}\n\tif len(l.Tags) != 3 {\n\t\tt.Errorf(\"getLinterOptions should find 3 tags: %v\\n\", l.Tags)\n\t}\n}\n\nfunc TestCommandLineOverridesProfile(t *testing.T) {\n\ttags := \"1,2,3,4\"\n\to := emptyCommandLineOptions()\n\to.Tags = &tags\n\tp := ProfileOptions{\n\t\tTags: []string{\"1\", \"2\", \"3\"},\n\t}\n\tl, err := getLinterOptions(o, p)\n\n\tif err != nil {\n\t\tt.Errorf(\"getLinterOptions should not return error: %s\\n\", err.Error())\n\t}\n\tif len(l.Tags) != 4 {\n\t\tt.Errorf(\"getLinterOptions should find 4 tags: %v\\n\", l.Tags)\n\t}\n}\n\nfunc TestCommandLineVariables(t *testing.T) {\n\to := emptyCommandLineOptions()\n\to.Variables = []string{\"namespace=web\"}\n\tp := ProfileOptions{}\n\tl, err := getLinterOptions(o, p)\n\n\tif err != nil {\n\t\tt.Errorf(\"getLinterOptions should not return error: %s\\n\", err.Error())\n\t}\n\tv, ok := l.Variables[\"namespace\"]\n\tif !ok {\n\t\tt.Errorf(\"Expecting namespace variable to have a value\\n\")\n\t} else {\n\t\tif v != \"web\" {\n\t\t\tt.Errorf(\"Expecting namespace variable to be 'web', not '%s'\\n\", v)\n\t\t}\n\t}\n}\n\nfunc TestMergeVariables(t *testing.T) {\n\to := emptyCommandLineOptions()\n\to.Variables = []string{\"namespace=web\"}\n\tp := ProfileOptions{\n\t\tVariables: map[string]string{\"kind\": \"Pod\"},\n\t}\n\tl, err := getLinterOptions(o, p)\n\n\tif err != nil {\n\t\tt.Errorf(\"getLinterOptions should not return error: %s\\n\", err.Error())\n\t}\n\tnamespace, ok := l.Variables[\"namespace\"]\n\tif !ok {\n\t\tt.Errorf(\"Expecting namespace variable to have a value\\n\")\n\t} else {\n\t\tif namespace != \"web\" {\n\t\t\tt.Errorf(\"Expecting namespace variable to be 'web', not '%s'\\n\", namespace)\n\t\t}\n\t}\n\tkind, ok := l.Variables[\"kind\"]\n\tif !ok {\n\t\tt.Errorf(\"Expecting kind variable to have a value\\n\")\n\t} else {\n\t\tif kind != \"Pod\" {\n\t\t\tt.Errorf(\"Expecting kind variable to be 'Pod', not '%s'\\n\", kind)\n\t\t}\n\t}\n}\n\nfunc TestLoadProfile(t *testing.T) {\n\tp, err := loadProfile(\"./testdata/profile.yml\")\n\tif err != nil {\n\t\tt.Errorf(\"Expecting loadProfile to run without error: %v\\n\", err.Error())\n\t}\n\tif len(p.Tags) != 1 || p.Tags[0] != \"iam\" {\n\t\tt.Errorf(\"Expecting single tag in profile: %v\\n\", p.Tags)\n\t}\n}\n\nfunc TestProfileExclude(t *testing.T) {\n\tp, err := loadProfile(\"./testdata/profile.yml\")\n\tif err != nil {\n\t\tt.Errorf(\"Expecting loadProfile to run without error: %v\\n\", err.Error())\n\t}\n\to := emptyCommandLineOptions()\n\tl, err := getLinterOptions(o, p)\n\tif err != nil {\n\t\tt.Errorf(\"Expecting getLinterOptions to run without error: %v\\n\", err.Error())\n\t}\n\tif len(l.ExcludePatterns) != 3 {\n\t\tt.Errorf(\"Expecting 3 excludes in total using 'exclude' and 'exclude_from' in profile: %v\\n\", l.ExcludePatterns)\n\t}\n\tif l.ExcludePatterns[0] != \"this_file_will_be_excluded.tf\" {\n\t\tt.Errorf(\"Expecting 1st pattern using 'exclude' in profile to be 'this_file_will_be_excluded.tf', not '%s'\", l.ExcludePatterns[0])\n\t}\n\tif l.ExcludePatterns[1] != \"*1.tf\" {\n\t\tt.Errorf(\"Expecting 2nd pattern using 'exclude_from' in profile to be '*1.tf', not '%s'\", l.ExcludePatterns[1])\n\t}\n\tif l.ExcludePatterns[2] != \"*2.tf\" {\n\t\tt.Errorf(\"Expecting 3rd pattern using 'exclude_from' in profile to be '*2.tf', not '%s'\", l.ExcludePatterns[2])\n\t}\n}\n\nfunc TestValidateParser(t *testing.T) {\n\tparser, err := validateParser(\"\")\n\tif err != nil {\n\t\tt.Errorf(\"Expected %s, got %v\", parser, err)\n\t}\n\tparser, err = validateParser(\"tf11\")\n\tif err != nil {\n\t\tt.Errorf(\"Expected %s, got %v\", parser, err)\n\t}\n\tparser, err = validateParser(\"tf12\")\n\tif err != nil {\n\t\tt.Errorf(\"Expected %s, got %v\", parser, err)\n\t}\n\tparser, err = validateParser(\"tf13\")\n\tif err == nil {\n\t\tt.Errorf(\"Expected %v, got nil\", err)\n\t}\n}\n"
  },
  {
    "path": "cli/report_writer.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\nfunc (w DefaultReportWriter) WriteReport(report assertion.ValidationReport, options LinterOptions) {\n\tif options.SearchExpression == \"\" {\n\t\terr := printReport(w.Writer, report, options.QueryExpression)\n\t\tif err != nil {\n\t\t\tfmt.Println(err.Error())\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "cli/report_writer_test.go",
    "content": "package main\n\nimport (\n\t\"bytes\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestReportWriter(t *testing.T) {\n\tvar b bytes.Buffer\n\tw := DefaultReportWriter{Writer: &b}\n\tr := assertion.ValidationReport{\n\t\tViolations: []assertion.Violation{\n\t\t\tassertion.Violation{\n\t\t\t\tRuleID: \"RULE_1\",\n\t\t\t},\n\t\t},\n\t}\n\tw.WriteReport(r, LinterOptions{})\n\tassert.Contains(t, b.String(), \"RULE_1\")\n}\n"
  },
  {
    "path": "cli/terraform_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/gobuffalo/packr\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stelligent/config-lint/linter\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype TestSuite struct {\n\tVersion     string\n\tDescription string\n\tType        string\n\tFiles       []string\n\tTests       []TestCase\n\tRootPath    string\n}\n\ntype TestCase struct {\n\tRuleId   string\n\tWarnings int\n\tFailures int\n\tTags     []string\n}\n\n/*\n*  Determine if the given filename is a test case\n */\nfunc isTestCase(filename string) bool {\n\tif strings.Contains(filename, \"test\") && isYamlFile(filename) {\n\t\treturn true\n\t} else {\n\t\treturn false\n\t}\n}\n\n/*\n* Given filepath to a YAML test suite, return a TestSuite object\n */\nfunc loadTestSuite(filename string) (TestSuite, error) {\n\tyamlFile, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to load Test Suite file: %v\\n %v\\n\", filename, err)\n\t\treturn TestSuite{}, err\n\t}\n\tts := TestSuite{}\n\terr = yaml.Unmarshal([]byte(yamlFile), &ts)\n\tif err != nil {\n\t\tassertion.Debugf(\"Failed to unmarshall YAML Test Suite: %v\\n %v\\n\", filename, err)\n\t\treturn TestSuite{}, err\n\t}\n\n\tts.RootPath = strings.TrimSuffix(filename, \"/tests/test.yml\")\n\n\treturn ts, nil\n}\n\nfunc getTestResources(directory string) ([]string, error) {\n\tvar testResources []string\n\tbox := packr.NewBox(directory)\n\tfilesInBox := box.List()\n\tconfigPatterns := []string{\"*.tf\"}\n\tfor _, f := range filesInBox {\n\t\tmatch, _ := assertion.ShouldIncludeFile(configPatterns, f)\n\t\tif match {\n\t\t\tabsolutePath, err := filepath.Abs(directory + \"/\" + f)\n\t\t\tif err != nil {\n\t\t\t\treturn []string{}, err\n\t\t\t}\n\t\t\ttestResources = append(testResources, absolutePath)\n\t\t}\n\t}\n\treturn testResources, nil\n}\n\nfunc contains(arr []string, str string) bool {\n\tfor _, a := range arr {\n\t\tif a == str {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// Run each test case in a test suite\nfunc runTestSuite(t *testing.T, ts TestSuite) {\n\t// Load only the rule for this test suite\n\truleConfigPath := strings.Split(ts.RootPath, \"config-lint/cli/assets/\")[1] + \"/rule.yml\"\n\truleSet, err := loadBuiltInRuleSet(ruleConfigPath)\n\tif err != nil {\n\t\tassert.Nil(t, err, \"Cannot load built-in Terraform rule\")\n\t}\n\n\tfor _, tc := range ts.Tests {\n\t\toptions := linter.Options{\n\t\t\tRuleIDs: []string{tc.RuleId},\n\t\t}\n\t\tvs := assertion.StandardValueSource{}\n\n\t\t// validate the rule set\n\t\tif contains(tc.Tags, \"terraform11\") {\n\t\t\t// Load the test resources for this test suite\n\t\t\ttestResourceDirectory := strings.Split(ts.RootPath, \"config-lint/cli/\")[1] + \"/tests/terraform11/\"\n\t\t\ttestResources, err := getTestResources(testResourceDirectory)\n\t\t\tif err != nil {\n\t\t\t\tassert.Nil(t, err, \"Cannot load built-in Terraform 11 test resources\")\n\n\t\t\t}\n\t\t\t// Defining 'tf11' for the Parser type\n\t\t\tl, err := linter.NewLinter(ruleSet, vs, testResources, \"tf11\")\n\n\t\t\treport, err := l.Validate(ruleSet, options)\n\t\t\tassert.Nil(t, err, \"Validate failed for file\")\n\n\t\t\twarningViolationsReported := getViolationsString(\"WARNING\", report.Violations)\n\t\t\twarningMessage := fmt.Sprintf(\"Expecting %d warnings for rule %s:\\n %s\", tc.Warnings, tc.RuleId, warningViolationsReported)\n\t\t\tassert.Equal(t, tc.Warnings, numberOfWarnings(report.Violations), warningMessage)\n\n\t\t\tfailureViolationsReported := getViolationsString(\"FAILURE\", report.Violations)\n\t\t\tfailureMessage := fmt.Sprintf(\"Expecting %d failures for rule %s:\\n %s\", tc.Failures, tc.RuleId, failureViolationsReported)\n\t\t\tassert.Equal(t, tc.Failures, numberOfFailures(report.Violations), failureMessage)\n\t\t}\n\n\t\tif contains(tc.Tags, \"terraform12\") {\n\t\t\t// Load the test resources for this test suite\n\t\t\ttestResourceDirectory := strings.Split(ts.RootPath, \"config-lint/cli/\")[1] + \"/tests/terraform12/\"\n\t\t\ttestResources, err := getTestResources(testResourceDirectory)\n\t\t\tif err != nil {\n\t\t\t\tassert.Nil(t, err, \"Cannot load built-in Terraform 12 test resources\")\n\n\t\t\t}\n\t\t\t// Defining 'tf11' for the Parser type\n\t\t\tl, err := linter.NewLinter(ruleSet, vs, testResources, \"tf12\")\n\n\t\t\treport, err := l.Validate(ruleSet, options)\n\t\t\tassert.Nil(t, err, \"Validate failed for file\")\n\n\t\t\twarningViolationsReported := getViolationsString(\"WARNING\", report.Violations)\n\t\t\twarningMessage := fmt.Sprintf(\"Expecting %d warnings for rule %s:\\n %s\", tc.Warnings, tc.RuleId, warningViolationsReported)\n\t\t\tassert.Equal(t, tc.Warnings, numberOfWarnings(report.Violations), warningMessage)\n\n\t\t\tfailureViolationsReported := getViolationsString(\"FAILURE\", report.Violations)\n\t\t\tfailureMessage := fmt.Sprintf(\"Expecting %d failures for rule %s:\\n %s\", tc.Failures, tc.RuleId, failureViolationsReported)\n\t\t\tassert.Equal(t, tc.Failures, numberOfFailures(report.Violations), failureMessage)\n\t\t}\n\t}\n}\n\n/*\n* Given resource type and tags\n* Run all defined tests per resource type and subtype\n */\nfunc RunBuiltinTests(t *testing.T, resourceType string) {\n\t// Get a list of all test cases\n\tbox := packr.NewBox(\"./assets/\" + resourceType)\n\tfilesInBox := box.List()\n\tfor _, file := range filesInBox {\n\t\tif isTestCase(file) {\n\t\t\tabsolutePath, _ := filepath.Abs(\"./assets/\" + resourceType + \"/\" + file)\n\t\t\tts, err := loadTestSuite(absolutePath)\n\t\t\tif err != nil {\n\t\t\t\tassert.Nil(t, err, \"Cannot load test case\")\n\t\t\t}\n\t\t\trunTestSuite(t, ts)\n\t\t}\n\t}\n}\n\n// Run built in rules against Terraform parser\nfunc TestTerraformBuiltInRules(t *testing.T) {\n\tRunBuiltinTests(t, \"terraform\")\n}\n"
  },
  {
    "path": "cli/testdata/builtin/terraform12/test.tf",
    "content": "resource \"aws_cloudtrail\" \"object_logging_enabled\" {\n  name                          = \"tf-trail-foobar\"\n  s3_bucket_name                = \"nwm-cloudtrail-logs\"\n  s3_key_prefix                 = \"prefix\"\n  include_global_service_events = false\n  event_selector {\n    read_write_type           = \"All\"\n    include_management_events = true\n    data_resource {\n      type   = \"AWS::S3::Object\"\n      values = [\"arn:aws:s3:::\"]\n    }\n  }\n}\n\nresource \"aws_cloudtrail\" \"object_logging_enabled\" {\n  name                          = \"tf-trail-foobar\"\n  s3_bucket_name                = \"nwm-cloudtrail-logs\"\n  s3_key_prefix                 = \"prefix\"\n  include_global_service_events = false\n  event_selector {\n    read_write_type           = \"All\"\n    include_management_events = true\n    data_resource {\n      type   = \"wrong\"\n      values = [\"arn:aws:s3:::\"]\n    }\n  }\n}\nresource \"aws_cloudtrail\" \"object_logging_enabled\" {\n  name                          = \"tf-trail-foobar\"\n  s3_bucket_name                = \"nwm-cloudtrail-logs\"\n  s3_key_prefix                 = \"prefix\"\n  include_global_service_events = false\n  event_selector {\n    read_write_type           = \"All\"\n    include_management_events = true\n  }\n}\n"
  },
  {
    "path": "cli/testdata/dirtest/a.yml",
    "content": "---\ncomment: test file\n"
  },
  {
    "path": "cli/testdata/dirtest/b.yml",
    "content": "---\ncomment: test file\n\n"
  },
  {
    "path": "cli/testdata/exclude-list",
    "content": "*1.tf\n*2.tf\n"
  },
  {
    "path": "cli/testdata/profile-exceptions.yml",
    "content": "---\n\nterraform: true\n\nfiles:\n  - \"*.tf\"\n\nexceptions:\n  - RuleID: IAM_ROLE_WILDCARD_ACTION\n    ResourceCategory: resource\n    ResourceType: aws_iam_role\n    ResourceID: role2\n    Comments: Just because\n\ntags:\n  - iam\n"
  },
  {
    "path": "cli/testdata/profile.yml",
    "content": "---\n\nterraform: true\n\nfiles:\n  - \"*.tf\"\n\nexceptions:\n  - RuleID: ROLE_WILDCARD_ACTION\n    ResourceCategory: resource\n    ResourceType: aws_iam_role\n    ResourceID: role2\n    Comments: Just because\n\ntags:\n  - iam\n\nexclude:\n  - this_file_will_be_excluded.tf\n\nexclude_from:\n  - ./testdata/exclude-list\n"
  },
  {
    "path": "cli/testdata/smoketest_exceptions.tf",
    "content": "resource \"aws_iam_role\" \"role2\" {\n  name = \"role2\"\n\n  assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"*\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "cli/testdata/smoketest_tf11.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.test_key.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_alb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = \"${aws_s3_bucket.test_bucket.bucket}\"\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n"
  },
  {
    "path": "cli/testdata/smoketest_tf12.tf",
    "content": "## Setup Helper\nresource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"test_bucket\" {\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = aws_kms_key.test_key.arn\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n}\n\n# Pass\nresource \"aws_alb\" \"access_logs_enabled_set_to_true\" {\n  access_logs {\n    bucket  = aws_s3_bucket.test_bucket.bucket\n    prefix  = \"foo\"\n    enabled = true\n  }\n}\n"
  },
  {
    "path": "cli/testdata/syntax-errors.yml",
    "content": "version:1\ndescription:YAML Syntax errors\ntype:Errors\n\n"
  },
  {
    "path": "cli/testdata/terraform.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: RULE_1\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    #category: resource\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: FAILURE\n"
  },
  {
    "path": "docs/README.md",
    "content": "# 🔍 config-lint 🔎\n\nA command line tool to validate configuration files using rules specified in YAML. The configuration files can be one of several formats: Terraform, JSON, YAML, with support for Kubernetes. There are built-in rules provided for Terraform, and custom files can be used for other formats.\n\n📓 [Documentation](https://stelligent.github.io/config-lint)\n\n👷 [Contributing](https://github.com/stelligent/config-lint/tree/master/CONTRIBUTING.md)\n\n🐛 [Issues & Bugs](https://github.com/stelligent/config-lint/issues)\n\n## Blog Posts\n\n✏️ [config-lint: Up and Running](https://stelligent.com/2020/04/17/config-lint-up-and-running/)\n\n✏️ [Development Acceleration Through VS Code Remote Containers](https://stelligent.com/2020/04/10/development-acceleration-through-vs-code-remote-containers-setting-up-a-foundational-configuration/)\n\n## Quick Start\n\nInstall the latest version of config-lint on macOS using [Homebrew](https://brew.sh/):\n\n``` bash\nbrew tap stelligent/tap\nbrew install config-lint\n```\n\nOr manually on Linux:\n\n``` bash\ncurl -L https://github.com/stelligent/config-lint/releases/latest/download/config-lint_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin config-lint\nchmod +rx /usr/local/bin/config-lint\n```\n\nRun the built-in ruleset against your Terraform files. For instance if you want to run config-lint against our [example files](https://github.com/stelligent/config-lint/tree/master/example-files):\n\n``` bash\nconfig-lint -terraform example-files/config\n```\n\nYou will see failure and warning violations in the output like this:\n``` bash\n[\n  {\n    \"AssertionMessage\": \"viewer_certificate[].cloudfront_default_certificate | [0] should be 'false', not ''\",\n    \"Category\": \"resource\",\n    \"CreatedAt\": \"2020-04-15T19:24:33Z\",\n    \"Filename\": \"example-files/config/cloudfront.tf\",\n    \"LineNumber\": 10,\n    \"ResourceID\": \"s3_distribution\",\n    \"ResourceType\": \"aws_cloudfront_distribution\",\n    \"RuleID\": \"CLOUDFRONT_MINIMUM_SSL\",\n    \"RuleMessage\": \"CloudFront Distribution must use TLS 1.2\",\n    \"Status\": \"FAILURE\"\n  },\n  ...\n```\n\nYou can find more install options in our [installation guide](install.md)."
  },
  {
    "path": "docs/conditions.md",
    "content": "# Conditions\n\nSometimes you want to apply a rule only to a subset of a resource type, based on some attribute in that resource. You can add a list of expressions using the condition attribute. These must all evaluate to true for the rule to be applied. The format for these expressions is the same as that used for the assertions list.\n\nIf no condition is specified then the rule will be applied to all matching resources.\n\nIt's possible to accomplish the same end thing by using additional expressions directly in the assertions for a rule. But logically it's a little bit different to decide whether or not to apply a rule and what that rule should check. It also reads a little better.\n\n### Example\n\n```\n...\n  - id: YAML_RULES_HAVE_RESOURCES_SECTION\n    message: RuleSet for YAML required resources section\n    resource: LintRuleSet\n    severity: FAILURE\n    conditions:\n      - key: type\n        op: eq\n        value: YAML\n    assertions:\n      - key: resources\n        op: present\n...\n```\n\n"
  },
  {
    "path": "docs/coverpage.md",
    "content": "<!-- _coverpage.md -->\n\n# config-lint <small>1.x</small>\n\na tool to validate configuration files against custom specifications\n\nterraform | kubernetes | yaml | json | csv\n\n[GitHub](https://github.com/stelligent/config-lint)\n[Get Started](#config-lint)"
  },
  {
    "path": "docs/css/style.css",
    "content": "/* COVER */\nsection.cover {\n    color: #fff;\n    background:url(../img/bg-pattern.png),linear-gradient(to left,#f4842b,#7b4397) !important;\n  }\n  \n  section.cover h1 {\n    font-size: 5rem;\n    font-weight: 600;\n  }\n  \n  section.cover h1 a span {\n    color: #fff;\n  }\n  \n  /* Cover buttons */\n  \n  section.cover .cover-main > p:last-child a {\n    border: 1px solid #fff;\n    color: #fff;\n  }\n  section.cover .cover-main > p:last-child a:last-child {\n    background-color: #fff;\n    color: var(--theme-color,#fff);\n  }\n  section.cover .cover-main > p:last-child a:last-child:hover {\n    color: var(--theme-color,#fff);\n    opacity: 0.8;\n  }\n"
  },
  {
    "path": "docs/design.md",
    "content": "# Design\n\n## Motivation\n\n* Static analysis of Terraform configuration files, similar to [cfn_nag](https://github.com/stelligent/cfn_nag) for CloudFormation templates\n* Analysis of Kubernetes spec files\n* Scanning of AWS resources already provisioned, via AWS API describe* calls\n* Processing of event data in AWS Config custom rules\n\nWhile the data source in each of these cases is different (files vs API calls vs event parameters), the application of the rules follows a similar pattern:\n\n* Extract a data element about a resource\n* Make assertions about the value or values found\n\n## Goals\n\n### Rules written in a DSL, rather than code\n\nUsing a DSL in YAML has some advantages: \n\n* Common format that is easy to read\n* Easy to add new rules\n* Easy to scan an existing rule set\n\nThe DSL was modeled after that found in [Cloud Custodian](https://github.com/capitalone/cloud-custodian). Instead of specifying filters to select resources, the DSL makes assertions about values discovered about resources.\n\nThe DSL does have the ability to invoke HTTP endpoints. This is intended for logic that is too complex to specify in the DSL.\n\n### Dynamic data\n\nIn addition to using data embedded directly in a rule, the rules should be able to reference dynamic data. Typical examples are lists of IP addresses, and EC2 instance types. \n\nThe DSL can reference HTTP endpoints or S3 objects for such dynamic data. This idea was also inspired by Cloud Custodian.\n\n### Not aimed at remediation. \n\nThe primary use case is as part of a CI/CD pipeline. If violations are detected, the exit code of the program is set so the pipeline can be terminated.\n\nThe JSON output of the tool can be read by a other tools to trigger notifications or automatic remediation.\n\n## Implementation \n\nThe tool itself is written in go, and is a self contained binary. This simplifies its use in pipelines as well as for local development.  \n\n### Built-in rules\n\nThe implementation includes a set of rules for Terraform that can be turned on with a command line option. These implement the same rules found in [cfn_nag](https://github.com/stelligent/cfn_nag) as well as those found in [terrascan](https://github.com/cesar-rodriguez/terrascan)\n\n### Packages\n\nThere are three packages in the repository:\n\n* cli\n* linter\n* assertion\n\n#### cli package\n\nThis processes command line arguments and loads project files before using the linter package to do the actual linting.\n\n#### linter package\n\nDefines a linter interface, and provides a factory function to create linters that can discover resources that will be analyzed.\nCurrently supported:\n\n* File based configurations - Terraform, Kubernetes, YAML, and self validation of config-lint rules files\n* API based congifurations - AWS Security Groups and IAM users\n\nThe cli package in this repo uses the linter package. The linter package might also be used in tools that are not command line driven, such as a website or an AWS Lambda used for Config rules. Implementations of these can be found in the early commit history of this repository, but future work will be moved to separate repositories.\n\nThe linter then uses the assertion package to analyze the collection of resources, and then generate a report.\n\n#### assertion\n\nApplies a set of rules to a collection of JSON objects and returns a report that includes any violations found. \nThis package works on JSON objects and has no knowledge of how the resources were loaded. That work is done in the linter package.\n\n"
  },
  {
    "path": "docs/development.md",
    "content": "# Developing for config-lint\n\n## VS Code Remote Development\nThe preferred method of developing is to use the VS Code Remote development functionality.\n\n- Install the VS Code [Remote Development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)\n- Open the repo in VS Code\n- When prompted \"`Folder contains a dev container configuration file. Reopen folder to develop in a container`\" click the \"`Reopen in Container`\" button\n- When opening in the future use the \"`config-lint [Dev Container]`\" option\n\n### VS Code Dependencies\n\nThere are a couple of dependencies that you need to configure locally before being able to fully utizlize the Remote Developemnt environment.\n- Requires `ms-vscode-remote.remote-containers` >= `0.101.0`\n- [Docker](https://www.docker.com/products/docker-desktop)\n  - Needs to be installed in order to use the remote development container\n- [GPG](https://gpgtools.org)\n  - Should to be installed in `~/.gnupg/` to be able to sign git commits with gpg\n- SSH\n  - Should to be installed in `~/.ssh` to be able to use your ssh config and keys.\n\n## Local Development\n\n### Prerequisites \n- [Install golang](https://golang.org/doc/install)\n- Add the output of the following command to your PATH\n```\necho \"$(go env GOPATH)/bin\"\n```\n\n### Build Command Line tool\n\n```\nmake all\n```\n\nThe binary is located at `.release/config-lint`\n\n### Tests\nTests are located in the `assertion` directory. To run all tests: \n```\nmake test\n```\n\nTo run the Terraform builtin rules tests:\n```\nmake testtf\n```\n\nMore information about how to create and run tests can be found [here](tests.md).\n\n### Linting\nTo lint all files (using golint):\n```\nmake lint\n```\n\n### Releasing\nMerging to master will automatically cut a minor incremental release for any code changes. To create a new major release, you will need to merge a commit that includes the `#major` tag.\n\nReleases are created via GitHub Workflows. You can find more information about this [here](/github_workflow.md)"
  },
  {
    "path": "docs/example-rules.md",
    "content": "# Example Rules\n\nAdd these rules to a YAML file, and pass the filename to config-lint using the -rules option.\nEach rule contains a list of assertions, and these assertions use operations that are [documented here](operations.md).\n\n\n* [Simple Expressions](#simple-expressions)\n* [Boolean Expressions](#boolean-expressions)\n* [Collection Expressions](#collection-expressions)\n* [Dynamic Values](#dynamic-values)\n* [Conditions](#conditions)\n* [Macros](#macros)\n\n## simple-expressions\n\nTo test that an AWS instance type has one of two values:\n\n```\nversion: 1\ndescription: Simple expression example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: EC2_INSTANCE_TYPE\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n```\n\n## boolean-expressions\n\nThis could also be done by using the or operation with two different assertions:\n\n```\nversion: 1\ndescription: Boolean expression example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nRules:\n  - id: EC2_INSTANCE_TYPE\n    message: Instance type shouldG be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      or:\n        - key: instance_type\n          op: eq\n          value: t2.micro\n        - key: instance_type\n          op: eq\n          value: m3.medium\n    severity: WARNING\n```\n\n## collection-expressions\n\nThere are three operators that simplify working with collections: [every](operations.md#every), [some](operations.md#some) and [none](operations.md#none).\nYou provide a JMESPath expression to extract the entire collection from the resource properties.\nThen a separate set of expressions are applied to each element of the collection.\nThe expressions are the same as used for rule assertions.\nThe every operator requires all elements to return true for the expression, the some operator requires at least one element to return true, and the none operator requires all of the elements to return false for the expression.\n\n\n```\nversion: 1\ndescription: Collection expression example\ntype: YAML\nfiles:\n  - \"*.config\"\n\nresources:\n  - type: customer\n    key: customers[]\n    id: id\n\nrules:\n\n  - id: CUSTOMER_LOCATIONS\n    message: Every customer location needs an address and a zip_code\n    resource: customer\n    severity: FAILURE\n    assertions:\n      - every:\n          key: locations\n          assertions:\n            - key: address\n              op: present\n            - key: zip_code\n              op: present\n\n```\n\n## dynamic-values\n\nInstead of including a list of values directly in the rules file, it can be retrieved\nfrom an S3 object at runtime. HTTP endpoints are also supported.\n\n```\nversion: 1\ndescription: Dynamic value example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: EC2_INSTANCE_TYPE\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from: s3://your-bucket/instance-types.txt\n    severity: FAILURE\n```\n\n## conditions\n\nRules always have a condition based on the resource type, but you can add additional conditions. Here is an example\nfrom the internal rule set used for the -validate option. This rule will only apply when a resource of type LintRuleSet\nhas a type equal to YAML. For that type of LintRuleSet, another attribute called resources must be present:\n\n```\nversion: 1\ndescription: Condition example\ntype: YAML\nfiles:\n  - *.config\nrules:\n  - id: YAML_RULES_HAVE_RESOURCES_SECTION\n    message: RuleSet for YAML required resources section\n    resource: LintRuleSet\n    severity: FAILURE\n    conditions:\n      - key: type\n        op: eq\n        value: YAML\n    assertions:\n      - key: resources\n        op: present\n```\n\n## macros\n\nBecause the rules are specified in YAML format, it is possible to use anchors and aliases as a simple kind of macro language to\neliminate duplicate expressions in a rule set. If you are familiar with Ruby on Rails configuration files, this might be familiar.\n\nHere is an example where the rules for different resource types check for the same attribute names.\nYou can use the \"&\" to define an anchor. In this example there\nare three of these: &has_name, &has_description and &has_name_and_description (the first two are actually used to\ndefine the third one). Elsewhere in the file you can use the \"*\" (alias) and the \"<<\" (merge key) to insert\nthese expressions into multiple rules, without copying the entire expression map.\n\n```\nversion: 1\ndescription: Macro example\ntype: YAML\nfiles:\n  - \"*.config\"\n\n# some anchors to keep rules DRY\n\nhas_name: &has_name\n  key: name\n  op: present\n\nhas_name: &has_description\n  key: description\n  op: present\n\nhas_name_and_description: &has_name_and_description\n  and:\n    - <<: *has_name\n    - <<: *has_description\n\n# resources to find in the config file\n\nresources:\n  - type: widget\n    key: widgets[]\n    id: id\n  - type: gadget\n    key: gadgets[]\n    id: name\n\n# rules to apply\n\nrules:\n\n  - id: WIDGET_PROPERTIES\n    message: Widget needs name and description\n    severity: FAILURE\n    resource: widget\n    assertions:\n      - <<: *has_name_and_description\n\n  - id: GADGET_PROPERTIES\n    message: Gadget needs name a description\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - <<: *has_name_and_description\n```\n\nThis feature of YAML can even be used for partial expressions. The JMESPath expression for find a specific tag in a Terraform resource is not particularly friendly, so that could be hidden in an anchor called \"has_tag\". Then a rule assertion can reference that, and include the tag name that is required.\n\n```\nVersion: 1\nDescription: Use an alias to hide a complicated JMESPath expression\nType: Terraform\nFiles:\n  - \"*.tf\"\n\nhas_tag: &has_tag\n  key: \"tags[]|[0].keys(@)\"\n  op: contains\n\nrules:\n\n  - id: HAS_NAME_TAG\n    message: Tags are required\n    resource: aws_ebs_volume\n    assertions:\n      - <<: *has_tag\n        value: Name\n```\n\nThe assertions and operations were inspired by those in Cloud Custodian: https://cloud-custodian.github.io/cloud-custodian/docs/\n"
  },
  {
    "path": "docs/faq.md",
    "content": "# FAQs\n\n1. With the newest version of config-lint being able to handle configuration files written in Terraform v0.12 syntax, is it still\nbackwards compatible with configuration files written in the Terraform v0.11?\n    - Yes the new version of config-lint is able to handle parsing Terraform configuration files written in both v0.11 and v0.12 syntax.\n    - To choose the between parsing Terraform 0.11 vs 0.12 syntax, you can pass in the flag option `-tfparser` followed\n    by either `tf11` or `tf12`. For example:\n        - `config-lint -tfparser tf12 -rules example_rule.yml example_config/example_file.tf`\n2. I'm running into errors when trying to run the newest version of config-lint against configuration files\nwritten in Terraform v0.12 syntax. Where should be the first place to check for resolving this?\n    - The first thing to check is to make sure you're passing in the correct `-tfparser` flag option.\n    Depending on which Terraform syntax the configuration file is written in, refer to the FAQ #1 above for \n    passing in the correct flag option values.\n    - For configuration files that contain Terraform v0.12 syntax, you should confirm that whatever rule.yml file/files you pass in\n    have the `type:` key set to `Terraform12`. For example in this rule.yml file:\n    ```\n   version: 1\n   description: Rules for Terraform configuration files\n   type: Terraform12\n   files:\n     - \"*.tf\"\n   rules:\n     - id: AMI_SET\n       message: Testing\n       resource: aws_instance\n       assertions:\n         - key: ami\n           op: eq\n           value: ami-f2d3638a\n    ```\n"
  },
  {
    "path": "docs/github_workflow.md",
    "content": "# GitHub Workflows\n\nThis project utilizes GitHub Workflows to run checks against pushed commits and to also control releases.\n\n## Configs\n\nThe configuration files for the Workflows are stored in the `.github/workflows/` directory. The Workflows are split up into 2 different types, `Build` and `Deploy`. More information about each can be found below:\n\n### Build\n\n`.github/workflows/build.yml`\n\nThere is a general catchall `Build` Workflow that is used against each push to the repository from any branch as long as the push **DOES NOT** contain a tag. This Workflow will download the `GO` module dependencies and run a `make test` against the pushed commit.\n\nThis `Build` Workflow is attached to Pull Requests as a Status Check to ensure all the tests are passing before code can be merged.\n\n### Deploy\n\nThere are 2 `Deploy` Workflow types, each is tied to a specific release type, **`Stable`** or **`Beta`**.\n\n#### Stable Release\n\n`.github/workflows/build_and_deploy.yml`\n\nThis Workflow will run on any push that contains a tag with `v*.*.*` but will ignore tags ending in `-beta`. This Workflow will download the `GO` module dependencies and run a `make test` against the pushed commit. Afterwards it will run the `release` stage and run `goreleaser release` utilizing the `.goreleaser.yml` configuration file.\n\nThe `goreleaser` step will create release files and create an actual `Release` in GitHub. It will also update the [stelligent/homebrew-tap](https://github.com/stelligent/homebrew-tap) to use the latest stable version stored in the [Formula/config-lint.rb](https://github.com/stelligent/homebrew-tap/blob/master/Formula/config-lint.rb) file\n\n#### Beta Release\n\n`.github/workflows/beta_build_and_deploy.yml`\n\nThis Workflow will run on any push that contains a tag with `v*.*.*-beta`. It is important to note that it must end in `-beta` for this beta release Workflow to trigger. This Workflow will download the `GO` module dependencies and run a `make test` against the pushed commit. Afterwards it will run the `beta release` stage and run `goreleaser release` utilizing the `.beta-goreleaser.yml` configuration file.\n\nThe `goreleaser` step will create release files and create a `Pre-Release` in GitHub. It will also update the [stelligent/homebrew-tap](https://github.com/stelligent/homebrew-tap) to use the latest pre-release version stored in the [Formula/beta/config-lint.rb](https://github.com/stelligent/homebrew-tap/blob/master/Formula/beta/config-lint.rb) file.\n\n---\nSome things to note within the `.beta-goreleaser.yml` file:\n\n``` yaml\nrelease:\n  prerelease: auto\n```\n\n* This allows GitHub to assign a `Pre-Release` labeled release since the semantic version ends in `-beta`\n\n``` yaml\nbrews:\n  -\n  ...\n    folder: Formula/beta\n```\n\n* Storing the beta release in a new directory specifically for beta releases in homebrew.\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\">\n    <title>config-lint: a tool to validate configuration files against custom specifications.</title>\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge,chrome=1\"/>\n    <meta name=\"description\" content=\"simplify the declaration and administration of the AWS resources necessary to support microservices.\">\n    <meta name=\"viewport\" content=\"width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0\">\n    <link rel=\"stylesheet\" href=\"//unpkg.com/docsify/lib/themes/vue.css\">\n    <link rel=\"stylesheet\" href=\"css/style.css\" type=\"text/css\">\n<!-- <link rel=\"shortcut icon\" href=\"favicon.ico\" type=\"image/x-icon\" /> -->\n  </head>\n  <body>\n    <div id=\"app\"></div>\n    <script>\n      window.$docsify = {\n        name: 'config-lint',\n        repo: 'https://github.com/stelligent/config-lint',\n        themeColor: '#D4582D',\n        auto2top: true,\n        coverpage: \"coverpage.md\",\n        loadSidebar: \"sidebar.md\",\n        subMaxLevel: 2,\n        'flexible-alerts': {\n          style: 'flat',\n        },\n      };\n      // on GH pages, we need to define a base path to load css and images\n      let repoBasePath = location.pathname.match(/\\/config-lint\\//)\n        ? '/config-lint/'\n        : '/';\n      window.$docsify.basePath = repoBasePath;\n    </script>\n    <script src=\"//unpkg.com/docsify/lib/docsify.min.js\"></script>\n    <!-- Add a simple Click to copy button to all preformatted code blocks -->\n    <script src=\"//cdn.jsdelivr.net/npm/docsify-copy-code\"></script>\n    <!-- Full text search -->\n    <script src=\"//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js\"></script>\n    <!-- Color coded warning/alert blocks -->\n    <script src=\"https://unpkg.com/docsify-plugin-flexible-alerts\"></script>\n  </body>\n</html>"
  },
  {
    "path": "docs/install.md",
    "content": "# config-lint installation guide\n\n## Homebrew\n\nYou can use [Homebrew](https://brew.sh/) to install the latest version:\n\n``` bash\nbrew tap stelligent/tap\nbrew install config-lint\n```\n\n## Docker\n\nYou can pull the latest image from [DockerHub](https://hub.docker.com/r/stelligent/config-lint):\n\n``` bash\ndocker pull stelligent/config-lint\n```\n\nIf you choose to install and run via `docker` you will need mount a directory to the running container so that it has access to your configuration files.\n\n``` bash\ndocker run -v /path/to/your/configs/:/foobar stelligent/config-lint -terraform /foobar/foo.tf\n\n# or \n\ndocker run --mount src=/path/to/your/configs/,target=/foobar,type=bind stelligent/config-lint -terraform /foobar/foo.tf\n```\n\nIf you are linting Kubernetes configuration files, you will need to reference the path to the Kubernetes rules accordingly.\n\nFor example if the `pwd` has rules and configuration files:\n```\ndocker run -v $(pwd):/foobar stelligent/config-lint -rules /foobar/path/to/rules/kubernetes.yml /foobar/path/to/configs\n```\n\nIf you don't have your own set of rules that you want to run against your Kubernetes configuration files, you can copy or download the example set from [example-files/rules/kubernetes.yml](https://github.com/stelligent/config-lint/blob/master/example-files/rules/kubernetes.yml).\n\n## Linux\n\n```\n# Install the latest version of config-lint\ncurl -L https://github.com/stelligent/config-lint/releases/download/latest/config-lint_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin config-lint\n\n# See https://github.com/stelligent/config-lint/releases for release versions\nVERSION=v1.0.0\ncurl -L https://github.com/stelligent/config-lint/releases/download/${VERSION}/config-lint_Linux_x86_64.tar.gz | tar xz -C /usr/local/bin config-lint\n\nchmod +rx /usr/local/bin/config-lint\n```\n\n## Windows\n\nDownload the [latest Windows release](https://github.com/stelligent/config-lint/releases/latest) for your platform and add the binary to your Windows PATH.\n"
  },
  {
    "path": "docs/operations.md",
    "content": "# Operations\n\nThe rules contain a list of expressions that use operations\n\n## Assertion Operations\n\n| Operation                              | Description                |\n|----------------------------------------|----------------------------|\n| [absent](#absent)                      | Absent                     |\n| [and](#and)                            | And                        |\n| [contains](#contains)                  | Contains                   |\n| [does-not-contain](#does-not-contain)  | Does Not Contain           |\n| [ends-with](#ends-with)                | Ends With                  |\n| [eq](#eq)                              | Equal                      |\n| [empty](#empty)                        | Empty                      |\n| [every](#every)                        | Every                      |\n| [has-properties](#has-properties)      | Has Properties             |\n| [in](#in)                              | In                         |\n| [is-array](#is-array)                  | Is Array                   |\n| [is-false](#is-false)                  | Is False                   |\n| [is-not-array](#is-not-array)          | Is Not Array               |\n| [is-true](#is-true)                    | Is True                    |\n| [ne](#ne)                              | Not equal                  |\n| [none](#none)                          | None                       |\n| [not](#not)                            | Not                        |\n| [not-contains](#does-not-contain)      | Does Not Contain           |\n| [not-empty](#not-empty)                | Not Empty                  |\n| [not-in](#not-in)                      | Not In                     |\n| [exactly-one](#exactly-one)            | Exactly One                |\n| [or](#or)                              | Or                         |\n| [present](#present)                    | Present                    |\n| [regex](#regex)                        | Regex                      |\n| [starts-with](#starts-with)            | Starts With                |\n| [some](#some)                          | Some                       |\n| [xor](#xor)                            | Xor                        |\n| [is-subnet](#is-subnet)                | Is Subnet                  |\n| [is-private-ip](#is-private-ip)        | Is Private IP              |\n| [exposed-hosts](#exposed-hosts)        | Number of hosts exposed to |\n\n## eq\n\nEqual\n\n###Example:\n\n```\n...\n  - id: VOLUME1\n    resource: aws_ebs_volume\n    message: EBS Volumes must be encrypted\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: eq\n        value: true\n...\n```\n\n## ne\n\nNot Equal\n\nExample:\n```\n...\n  - id: SG1\n    resource: aws_security_group\n    message: Security group should not allow ingress from 0.0.0.0/0\n    severity: FAILURE\n    assertions:\n      - key: \"ingress[].cidr_blocks[] | [0]\"\n        op: ne\n        value: \"0.0.0.0/0\"\n...\n```\n\n## in\n\n In list of values\n\n### Example:\n\n```\n...\n  - id: R1\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n...\n```\n\n## not-in\n\nNot in list of values\n\n## present\n\nAttribute is present\n\n###Example:\n\n```\n...\n  - id: R6\n    message: Department tag is required\n    resource: aws_instance\n    assertions:\n      - key: \"tags[].Department | [0]\"\n        op: present\n    severity: FAILURE\n...\n```\n\n## absent\n\nAttribute is not present\n\n## empty\n\nAttribute is empty\n\n## not-empty\n\nAttribute is not empty\n\n## contains\n\nAttribute contains a substring, or array contains an element\n\n## does-not-contain\n\nAttribute does not contain a substring, or array does not contain an element\n\nYou can also use the 'not-contains' operator to do the same thing.\n\n## regex\n\nAttribute matches a regular expression\n\nSee [here](https://github.com/google/re2/wiki/Syntax) for regular expression syntax.\n\n## and\n\nLogical and of a list of assertions\n\n### Example:\n\n```\n...\n  - id: ANDTEST\n    resource: aws_instance\n    message: Should have both Project and Department tags\n    severity: WARNING\n    assertions:\n      - and:\n        - key: \"tags[].Department | [0]\"\n          op: present\n        - key: \"tags[].Project | [0]\"\n          op: present\n    tags:\n      - and-test\n...\n```\n\n## or\n\nLogical or of a list of assertions\n\n### Example:\n\n```\n...\n  - id: ORTEST\n    resource: aws_instance\n    message: Should have instance_type of t2.micro or m3.medium\n    severity: WARNING\n    assertions:\n      - or:\n        - key: instance_type\n          op: eq\n          value: t2.micro\n        - key: instance_type\n          op: eq\n          value: m3.medium\n...\n```\n\n## xor\n\nLogical xor of a list of assertions. The assertion is true when exactly one test passes\n\n### Example:\n\n```\n...\n  - id: ORTEST\n    resource: lint_rule\n    message: Can have value or value_from, but not both\n    severity: WARNING\n    assertions:\n      - xor:\n        - key: value\n          op: present\n        - key: value_from\n          op: present\n...\n```\n\n## not\n\nLogical not of an assertion\n\nExample:\n\n```\n...\n  - id: NOTTEST\n    resource: aws_instance\n    message: Should not have instance type of c4.large\n    severity: WARNING\n    assertions:\n      - not:\n        - key: instance_type\n          op: eq\n          value: c4.large\n...\n```\n\n## has-properties\n\nChecks for the present of every property in a comma separated list. This could also be done using the [and](#and) expression,\nbut this will often be more convenient.\n\nExample:\n\n```\n...\n  - id: VALID_ADDRESS\n    message: Every address needs city, state and zip\n    severity: FAILURE\n    resource: address\n    assertions:\n      - key: address\n        op: has-properties\n        value: city,state,zip\n...\n```\n\n## every\n\nSelect an array from a resource, and run assertions against each element. All of the sub assertions must pass for the test to pass.\nThe key is a JMESPath expression that should return an array of objects. The key used in each sub assertion is relative to the selected objects.\n\nThis provides a simple looping mechanism that is easier to write and understand than a complex JMESPath expression.\n\nExample:\n\n```\n...\n  - id: LOCATIONS_NEED_LAT_LONG\n    message:  Every location requires a latitude and longitude\n    severity: FAILURE\n    resource: sample\n    assertions:\n      - every:\n          key: Location\n          expressions:\n            - key: latitude\n              op: present\n            - key: longitude\n              op: present\n...\n```\n\n## some\n\nSelect an array from a resource, and run assertions against each element. At least one sub assertion must pass for the test to pass.\nThe key is a JMESPath expression that should return an array of objects. The key used in each sub assertion is relative to the selected objects.\n\nThis provides a simple looping mechanism that is easier to write and understand than a complex JMESPath expression.\n\nExample:\n\n```\n...\n  - id: LOCATION_REQUIRES_LAT_LONG\n    message:  At least one location requires a latitude and longitude\n    severity: FAILURE\n    resource: sample\n    assertions:\n      - some:\n          key: Location\n          expressions:\n            - key: latitude\n              op: present\n            - key: longitude\n              op: present\n...\n```\n\n## none\n\nSelect an array from a resource, and run assertions against each element. All of the sub assertions must fail for the test to pass.\nThe key is a JMESPath expression that should return an array of objects. The key used in each sub assertion is relative to the selected objects.\n\nThis provides a simple looping mechanism that is easier to write and understand than a complex JMESPath expression.\n\nExample:\n\n```\n...\n  - id: PORT_22_INGRESS\n    message:  No ingress for port 22 should be open to the world\n    severity: FAILURE\n    resource: sample\n    assertions:\n      - none:\n          key: \"ipPermissions[]\"\n          expressions:\n            - key: \"fromPort\"\n              op: eq\n              value: 22\n              value_type: integer\n            - key: \"ipRanges[]\"\n              op: contains\n              value: 0.0.0.0/0\n...\n```\n\n## exactly-one\n\nSelect an array from a resource, and run assertions against each element. Only one of the sub assertions should return true for the test to pass.\nThe key is a JMESPath expression that should return an array of objects. The key used in each sub assertion is relative to the selected objects.\n\nThis provides a simple looping mechanism that is easier to write and understand than a complex JMESPath expression.\n\nExample:\n\n```\n  - id: ONLY_ONE_DEFAULT\n    message:  Default should be true for only one element\n    severity: FAILURE\n    resource: sample\n    assertions:\n      - exactly-one:\n          key: \"items[]\"\n          expressions:\n            - key: \"default\"\n              op: is-true\n```\n\n## is-true\n\nCheck that the data has a true value. Shorthand for using { op: \"eq\" , \"value\": true }\n\nExample:\n\n```\n...\n  - id: ENCRYPTION_TRUE\n    message: Encryption should be true\n    severity: FAILURE\n    resource: ebs_volume\n    assertions:\n       - key: encrypted\n         op: is-true\n...\n```\n\n## is-false\n\nCheck that the data has a false value. Shorthand for using { op: \"eq\" , \"value\": false }\n\nExample:\n\n```\n...\n  - id: ALLOW_PUBLIC_ACCESS\n    message: Should not allow public access\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: public_access\n         op: is-false\n...\n```\n\n## starts-with\n\nCheck that a string value starts with a value\n\nExample:\n\n```\n...\n  - id: NAME_PREFIX\n    message: Name should have a certain prefix\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: name\n         op: starts-with\n         value: Foo\n...\n```\n\n## ends-with\n\nCheck that a string value ends with a value\n\nExample:\n\n```\n...\n  - id: NAME_SUFFIX\n    message: Name should have a certain suffix\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: name\n         op: ends-with\n         value: Resource\n...\n```\n\n## is-array\n\nCheck that an attribute is an array\n\nExample:\n\n```\n...\n  - id: TAGS_ARRAY\n    message: Tags should be an array\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: Tags[]\n         op: is-array\n...\n```\n\n## is-not-array\n\nCheck that an attribute is not an array\n\nExample:\n\n```\n...\n  - id: DESCRIPTION_NOT_AN_ARRAY\n    message: Description should not be an array\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: Name\n         op: is-not-array\n...\n```\n\n## is-subnet\n\nCheck whether a given IP or CIDR block is a subnet of a larger CIDR  block\n\nExample:\n\n```\n...\n  - id: IP_IN_SUPERNET\n    message: All ip_address values should be in the 10.0.0.0/8 supernet\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: \"ip_address\"\n         op: is-subnet\n         value: \"10.0.0.0/8\"\n...\n```\n\n## is-private-ip\n\nCheck whether a given IP is in RFC1918 address space.\n\nExample:\n\n```\n...\n  - id: IP_IN_PRIVATE_RANGE\n    message: All ip_address values should be in private address space\n    severity: FAILURE\n    resource: some_resource\n    assertions:\n       - key: \"ip_address\"\n         op: is-private-ip\n...\n```\n\n## max-host-count\n\nChecks how many hosts in a given CIDR range. This is useful for evaluating security group rules, for instance.\n\nExample:\n\n```\n...\n  - id: MAX_HOSTS_EXPOSED_PER_RULE\n    message: All security group rules must expose less than 1016 hosts\n    severity: FAILURE\n    resource: aws_security_group_rule\n    assertions:\n      - every:\n        key: \"cidr_blocks\"\n        expressions:\n          - key: \"@\"\n            op: max-host-count\n            value: 1016\n...\n```\n"
  },
  {
    "path": "docs/output.md",
    "content": "# Output from config-lint\n\nThe program outputs a JSON string with the results. The JSON object has the following attributes:\n\n* FilesScanned - a list of the filenames evaluated\n* Violations - an object whose keys are the severity of any violations detected. The value for each key is an array with an entry for every violation of that severity.\n\n## Using -query to limit the output\n\nYou can limit the output by specifying a JMESPath expression for the -query command line option. For example, if you just wanted to see the ResourceId attribute for failed checks, you can do the following:\n\n```\n./config-lint -rules example-files/rules/terraform.yml -query 'Violations.FAILURE[].ResourceId' example-files/config/*\n```"
  },
  {
    "path": "docs/profiles.md",
    "content": "# Profiles\n\nYou can use a profile to control the default options.\n\nThe -profile command line option takes a filename which contains a set of default values for various command line options. If there is a file in the working directory called `config-lint.yml`, it will be loaded automatically. All values in the profile are optional, and are overriden by anything specified on the command line.\n\nAn example profile:\n\n```\n# A list of files containing rules for linting\nrules:\n  - example-files/rules/generic-yaml.yml\n\n# A list of files to scan\nfiles:\n  - example-files/config/*.config\n\n# An optional list of rules to check, the default is all rules\nids:\n  - RULE_1\n  - RULE_2\n\n# An optional list of tags used to select what rules to apply, the default is all rules\ntags:\n  - s3\n\n# A list of resources and rules that should not be applied\n# This is useful if you want to turn off some rules for some resources, especially\n# when using built-in rules\n# (For custom rules files, you can use the Except attribute on a rule)\nexceptions:\n  - RuleID: S3_BUCKET_ACL\n    ResourceCategory: resource\n    ResourceType: aws_s3_bucket\n    ResourceID: simple_website\n    Comments: This bucket hosts a public website\n```\n\n"
  },
  {
    "path": "docs/rule_development.md",
    "content": "# Developing rules for config-lint\n\n## Developing new rules using -search\n\nEach rule requires a JMESPath key that it will use to search resources. Documentation for JMESPATH is here: http://jmespath.org/\n\nThe expressions can be tricky to get right, so this tool provides a -search option which takes a JMESPath expression. The expression is evaluated against all the resources in the files provided on the command line. The results are written to stdout.\n\nThis example will scan the example terraform file and print the \"ami\" attribute for each resource:\n\n```\n./config-lint -rules example-files/rules/terraform.yml -search 'ami' example-files/config/terraform.tf\n```\n\nIf you specify -search, the rules files is only used to determine the type of configuration files.\nThe files will *not* be scanned for violations."
  },
  {
    "path": "docs/rules.md",
    "content": "# Rules File\n\nA YAML file that specifies what kinds of files to process, and what validations to perform.\n\n## Attributes for the Rule Set\n\n|Name       |Description                                                                         |\n|-----------|------------------------------------------------------------------------------------|\n|version    |Currently ignored                                                                   |\n|description|Text description for the file, not currently used                                   |\n|type       |Terraform, Terraform12, Kubernetes, SecurityGroups, AWSConfig                                    |\n|files      |Filenames must match one of these patterns to be processed by this set of rules     |\n|rules      |A list of rules, see next section                                                   |\n\n## Attributes for each Rule\n\nEach rule contains the following attributes:\n\n|Name             |Description                                                                         |\n|-----------------|------------------------------------------------------------------------------------|\n|id               | A unique identifier for the rule                                                   |\n|message          | A string to be printed when a validation error is detected                         |\n|resource         | The resource type to which the rule will be applied                                |\n|resources        | A list of resources types to which the rule will be applied                        |\n|except_resources | A list of resource types to exclude                                                |\n|category         | Optional value used for Terraform: resource(default), data, provider               |\n|[conditions](conditions.md)       | Expressions (in addition to resource) that determine if a rule should apply        |\n|except           | An optional list of resource ids that should not be validated                      |\n|severity         | FAILURE, WARNING, NON_COMPLIANT                                                    |\n|assertions       | A list of expressions used to detect validation errors, see next section           |\n|invoke           | Alternative to assertions for a custom external API call to validate, see below    |\n|tags             | Optional list of tags, command line has option to limit scans to a subset of tags  |\n\n## Attributes for each Expression\n\nEach expression contains the following attributes:\n\n|Name       |Description                                                                         |\n|-----------|------------------------------------------------------------------------------------|\n|key        | JMES path used to find data in a resource                                          |\n|[op](operations.md)         | Operation to perform on the data return. [See here for valid operations](operations.md) |\n|value      | Literal value needed for most operations                                           |\n|[value_from](value_from.md) | Endpoint for loading values dynamically [See here for dynamic values](value_from.md) |\n\n## Invoke external API for validation\n\n|Name       | Description                                                                        |\n|-----------|------------------------------------------------------------------------------------|\n|Url        | HTTP endpoint to invoke                                                            |\n|Payload    | Optional JMESPATH to use for payload, default is '@'                               |\n\n"
  },
  {
    "path": "docs/running.md",
    "content": "# Running config-lint\n\nThe program has a set of built-in rules for scanning the following types of files:\n\n* [Terraform](terraform.md)\n\nThe program can also read files from a separate YAML file, and can scan these types of files:\n\n* [Terraform](terraform.md)\n* Kubernetes\n* LintRules\n* YAML\n* JSON\n\n## Example invocations\n\n### Validate Terraform files with built-in rules\n\n```\nconfig-lint -terraform example-files/config\n```\n\n### Validate Terraform files with custom rules\n\n```\nconfig-lint -rules examples-files/rules/terraform.yml example-files/config\n```\n\n### Validate Kubernetes files\n\n```\nconfig-lint -rules example-files/rules/kubernetes.yml example-files/config\n```\n\n### Validate LintRules files\n\nThis type of linting allows the tool to lint its own rules.\n\n```\nconfig-lint -rules example-files/rules/lint-rules.yml example-files/rules\n```\n\n### Validate a custom YAML file\n\n```\nconfig-lint -rules example-files/rules/generic-yaml.yml example-files/config/generic.config\n```\n\n## Using STDIN\n\nYou can use \"-\" for the filename if you want the configuration data read from STDIN.\n\n```\ncat example-files/resources/s3.tf | config-lint -terraform -\n```\n\n## Exit Code\n\nIf at least one rule with a severity of FAILURE was triggered the exit code will be 1, otherwise it will be 0.\n\n## Options\n\nHere are all the different command line options that can be used with config-lint. You can also\nview them via the -help option.\n\n * -debug - Debug logging\n    \t\n * -exclude value - Filename patterns to exclude\n \n * -exclude-from value - Filename containing patterns to exclude\n \n * -ids string - Run only the rules in this comma separated list\n \n * -ignore-ids string - Ignore the rules in this comma separated list\n \n * -profile string- Provide default options\n \n * -query string - JMESPath expression to query the results\n \n * -rules value - Rules file, can be specified multiple times\n \n * -search string - JMESPath expression to evaluation against the files\n \n * -tags string - Run only tests with tags in this comma separated list\n \n * -terraform - Use built-in rules for Terraform\n \n * -validate - Validate rules file\n \n * -var value - Variable values for rules with ValueFrom.Variable\n \n * -verbose - Output a verbose report\n \n * -version - Get program version\n \n * -tfparser - (Optional) Set the Terraform parser version. Options are `tf11` or `tf12`. By default, `tf12` will be used."
  },
  {
    "path": "docs/sidebar.md",
    "content": "<!-- _sidebar.md -->\n\n- [home](/)\n- [installation guide](install.md 'Installation guide')\n- [running config-lint](running.md 'Running config-lint')\n- [profiles](profiles.md 'Profiles to specify default config-lint options')\n- [output](output.md 'Understanding the output from config-lint')\n- [example rules](example-rules.md 'Some examples of config-lint rules')\n- [design](design.md 'Overview of config-lint design')\n- [rule development](rule_development.md 'Rule development')\n- [rules](rules.md 'Structure of config-lint rules files')\n- [tests](tests.md 'Testing config-lint rules')\n- [terraform](terraform.md 'Terraform linting')\n- [yaml](yaml.md 'Parsing an abitrary YAML file')\n- [operations](operations.md 'Rule operations')\n- [conditions](conditions.md 'Rule conditions')\n- [value_from](value_from.md 'Using dynamic values')\n- [development](development.md 'config-lint development information')\n- [github workflow](github_workflow.md 'GitHub workflows')\n- [FAQ](faq.md 'Frequently asked questions')"
  },
  {
    "path": "docs/terraform.md",
    "content": "# Terraform Linting\n\n## Validate Terraform files with built-in rules\n\nThere is a set of [built-in rules](/cli/assets/terraform) that cover some best practices for AWS resources.\n\n```\nconfig-lint -terraform <FILE_OR_DIRECTORY_OF_TF_FILES>\n```\n\nIf you want to run most of the built-in rules, but not all, you can use a [profile](profiles.md) to exclude some rules or resources.\n\nThe Terraform12 parser is fully backwards compatible with previous versions of Terraform. By default, Terraform files will be validated with Terraform 0.12 standards. \n\nIf you wish to force a specific parser version, add the `-tfparser tf11|tf12` flag. This is useful if you have a lot of rules with `Type: Terraform` but your Terraform files include Terraform 12 syntax. \n\n## Custom Terraform rules for your project or organization\n\n```\nconfig-lint -rules <CUSTOM_RULE_YML_FILE> <FILE_OR_DIRECTORY_OF_TF_FILES>\n```\n\nYou can specify the -rules option multiple times if you have multiple custom rule files. It is also possible to specify both the -terraform option as well as one or more -rules options, if you want the built-in rules as well as some custom rules.\n\n### Categories\n\nThe default category for resources that can be linter is \"resource\", which covers the most common use case. This is for things like aws_instances, or s3_buckets, etc. But all other block types are available for Terraform linting.\n\n* data\n* locals\n* module\n* output\n* provider\n* resource\n* terraform\n* variable\n\n### Rule Structure\n\nRules are divided into their respective resource directory starting under `assets/terraform`. Each rule is organized following the same tiered directory structure `{ Provider }} / {{ Major Family }} / {{ Resource Name }} / {{ Rule Name }} / rule.yml` where Major Family and Resource Name follow the same naming conventions defined by Terraform. For example, `cli/assets/terraform/aws/batch/batch_job_definition/container_properties_privileged/rule.yml`. The rule configuration itself must be named `rule.yml`.\n\n```\n└── terraform\n    ├── aws\n    │   ├── batch\n    │   │   └── batch_job_definition\n    │   │       └── container_properties_privileged\n    │   │           ├── rule.yml\n    │   │           └── tests\n    │   │               ├── terraform11\n    │   │               │   └── container_properties_privileged.tf\n    │   │               ├── terraform12\n    │   │               │   └── container_properties_privileged.tf\n    │   │               └── test.yml\n    ...\n```\n\n\n### Rule Example\n\n```yaml\n---\nversion: 1\ndescription: Check for tags in Terraform file\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: REQUIRED_TAGS\n    message: \"A required tag is missing\"\n    resources:\n      - aws_s3_bucket\n      - aws_instance\n    assertions:\n      - key: tags[0]\n        op: has-properties\n        value: environment,cost_center\n  - id: VALID_ENVIRONMENT_TAG\n    message: \"The environment tag is not valid\"\n    resources:\n      - aws_s3_bucket\n      - aws_instance\n    assertions:\n      - key: tags[0].environment\n        op: in\n        value: dev,prod,stage\n  - id: VALID_COST_CENTER_TAG\n    message: \"Cost center must be a 4 digit number\"\n    resources:\n      - aws_s3_bucket\n      - aws_instance\n    assertions:\n      - key: tags[0].cost_center\n        op: regex\n        value: \"^[0-9]{4}$\"\n```\n\n### Top level blocks\n\nConfig-lint mainly works on [block arguments and expressions](https://www.terraform.io/docs/configuration/index.html#arguments-blocks-and-expressions) level (the inner part of Terraform blocks), however top level block types and names could linted using `__type__` and `__name__` keys.\n\nHere is an example to follow [Terraform best practices](https://www.terraform.io/docs/extend/best-practices/naming.html) for naming:\n\n```yaml\n---\nversion: 1\ndescription: Make sure Terraform top level blocks follow best practices.\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: TF_RESOURCE_NAMING_CONVENTION\n    message: \"Terraform resource block name should match the naming convention. Name should be: not more 64 chars, starts with letter, doesn't have dash, and ends with letter or number\"\n    severity: FAILURE\n    category: resource\n    assertions:\n    - key: __name__\n      op: regex\n      value: '^[a-z][a-z0-9_]{0,62}[a-z0-9]$'\n  - id: TF_DATA_NAMING_CONVENTION\n    message: \"Terraform data block name should match the naming convention. Name should be: not more 64 chars, starts with letter, doesn't have dash, and ends with letter or number\"\n    severity: FAILURE\n    category: data\n    assertions:\n    - key: __name__\n      op: regex\n      value: '^[a-z][a-z0-9_]{0,62}[a-z0-9]$'\n```\n\nAnother example, maybe you want to make sure there are no beta providers (e.g. [google-beta](https://www.terraform.io/docs/providers/google/guides/provider_versions.html#google-beta)) used in production:\n```yaml\nrules:\n  - id: TF_PROVIDER_NO_BETA\n    message: \"No beta feature providers should be used in production\"\n    severity: FAILURE\n    category: data\n    assertions:\n    - not:\n      - key: __type__\n        op: regex\n        value: '.*?beta.*'\n```\n\n**Please note:**\n\nNot all blocks have `__type__` and `__name__` keys. It depends on the block itself. For example, `variable` blocks have name but not type.\n\nThere are 4 groups in that regard:\n* **Type and name:** data, resource.\n* **Type only:** provider.\n* **Name only:** module, output, variable.\n* **No type and no name:** locals, terraform (they are linted using normal keys defined within them).\n\n### Provider Example\n\nFor providers, set the category to \"provider\" and the resource attribute to the name of the provider.\n\n```yaml\n---\nversion: 1\ndescription: Terraform provider example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: NO_SECRETS_IN_AWS_PROVIDER\n    category: provider\n    resource: aws\n    assertions:\n      - key: access_key\n        op: absent\n      - key: secret_key\n        op: absent\n```\n\n### Module Example\n\nFor modules, use \"module\" for category, and for resource use the \"source\" attribute.\n\nThis allows checking of parameters being used when a module is referenced.\n\n\n```yaml\n---\nversion: 1\ndescription: Terraform module invocation example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: MODULE_EXAMPLE\n    message:\n    category: module\n    resource: \"example/website\"\n    assertions:\n      - key: num_servers\n        op: present\n```\n\n### Terraform 12 Example\n\nNote the `type: Terraform12` item below. Rules targeting templates with Terraform 12-specific features must use the Terraform12 type.\n\n```yaml\nversion: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform12\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: CIDR_SET\n    message: Testing\n    resource: aws_security_group\n    assertions:\n      - every:\n          key: \"ingress\"\n          expressions:\n            # this says that it either must be a private IP, or not have IP regex (eg sg string, interpolation)\n            - every:\n                key: cidr_blocks\n                expressions:\n                  - key: \"@\"\n                    op: contains\n                    value: \"/24\"\n```\n\n### Evaluating Terraform 12 Dynamic Blocks\n\nDynamic blocks are a new feature introduced in Terraform 12 that enables users to dynamically construct repeatable nested blocks such as ingress rules in an AWS Security Group.\n\nWriting rules for dynamic blocks is a little tricky, as the structure that Terraform parses the .tf file into is different than you may expect.\n\nThis Terraform config will generate an `ingress` block for reach item in the `service_ports` list variable.\n```hcl-terraform\nvariable \"service_ports\" {\n  default = [22, 80, 1433, 6379]\n}\n\nresource \"aws_security_group\" \"example\" {\n  name = \"example\"\n\n  dynamic \"ingress\" {\n    for_each = var.service_ports\n    content {\n      from_port = ingress.value\n      to_port   = ingress.value\n      protocol  = \"tcp\"\n    }\n  }\n\n  egress = \"-1\"\n}\n```\n\nThe following rule will result in an error if port 22 (SSH) is included as an ingress for the security group.\n\nThe JMESPATH expression refers to keys (\"dynamic\" and \"for_each\") that are generated by Terraform, rather than what is present in the configuration.\n\n```yaml\nversion: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform12\nfiles:\n  - \"dynamic_block.tf\"\nrules:\n  - id: NO_SSH_ACCESS\n    message: Testing\n    resource: aws_security_group\n    assertions:\n      - key: \"dynamic[*].for_each[]\"\n        op: not-contains\n        value: 22\n```\n\n### Testing Builtin Rules\n\nAll rules need to be tested. All tests for a given rule will be included at the same rule path as the rule configuration itself and live under the `tests` folder. That test folder must include a configuration for the tests, named `test.yml`, and the resources required for testing. The test configuration file must follow the following format:\n\n```yaml\n---\nversion: 1\ndescription: Terraform 11 and 12 tests\ntype: Terraform\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\ntests:\n  -\n    ruleId: RULE_1_TO_BE_TESTED\n    warnings: 0\n    failures: 2\n    tags:\n      - \"terraform11\"\n      - \"terraform12\"\n  -\n    ruleId: RULE_2_TO_BE_TESTED\n    warnings: 2\n    failures: 0\n    tags:\n      - \"terraform11\"\n  -\n    ruleId: RULE_2_TO_BE_TESTED\n    warnings: 3\n    failures: 0\n    tags:\n      - \"terraform12\"\n```\n\nThe `ruleId` must match the RuleID given in the rule configuration. Warnings or Failures will check against any resource files included under the named `tag` directory within the same tests directory. For example `RULE_1_TO_BE_TESTED` will run the rule against resources in both folders terraform11 and terraform12 and check for the same number of warnings and failures for both. Whereas `RULE_2_TO_BE_TESTED` will check for a different number of warnings for the two versions.\n"
  },
  {
    "path": "docs/tests.md",
    "content": "# Tests\n\nYou can run the project tests by invoking the correct make command:\n\n* `make test` -> Runs all tests residing inside the config-lint project\n* `make testtf` -> Runs all tests defined within the `TestTerraformBuiltInRules` test function. This runs against terraform 0.11 first and then against terraform 0.12\n\n## Testing Best Practices\n\nIt is best practice to always come up with **at least 2** scenarios for each test (ideally more if applicable). You want a test case that will *pass* and a test case that will *fail*. This covers the bare minimum to ensure that a rule and test case are working as expected.\n\n## Terraform\n\nTerraform rules have their own set of tests that you can use to verify that a new rule or configuration is working as expected. As noted above, the `make testtf` command will run the tests defined within the `TestTerraformBuiltInRules` test function.\n\n### Creating Terraform Tests\n\nTo create a new test to validate a Terraform built in rule you need to do the following:\n* Add test case inside `TestTerraformBuiltInRules` function in the `cli/builtin_terraform_test.go` file.\n  * Example: `{\"aws/security_group/world_ingress.tf\", \"SG_WORLD_INGRESS\", 2, 0},` will run the `SG_WORLD_INGRESS` rule against the contents of the `cli/testdata/builtin/terraform/aws/security_group/world_ingress.tf` file. It is expecting there to be **2** *Warnings* and **0** *Failures*\n  * The test must follow the `struct` format for `BuiltInTestCase`\n\n  ``` go\n  type BuiltInTestCase struct {\n\tFilename     string\n\tRuleID       string\n\tWarningCount int\n\tFailureCount int\n  }\n  ```\n\n  * This test case must match a valid Rule `id` from within the `cli/assets/terraform.yml` file.\n"
  },
  {
    "path": "docs/value_from.md",
    "content": "# Dynamic Value\n\nIn cases where a rule needs a dynamic value, instead of specifying \"value\", \"value_from\" can be used instead. This allows an external source, such as an S3 object, or an HTTP endpoint to provider the values. Or the value can be provided on the command line when config-lint is invoked.\n\n\n## Using an S3 Bucket\n\n\n### Example:\n\n```\n...\n  - id: VALUE_FROM_S3\n    message: Instance type should be in list from S3 object\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          url: s3://my-bucket/allowed-instance-types\n...\n```\n\n## Using an HTTP endpoint\n\n### Example:\n\n```\n...\n  - id: VALUE_FROM_HTTPS\n    message: Instance type should be in list from https endpoint\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          url: https://my-api-endpoint/dev/instance_types\n...\n```\n\n## Using a command line variable\n\n### Example:\n\n```\n...\n  - id: VALUE_FROM_COMMAND_LINE\n    message: Instance type should be in list from https endpoint\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          variable: instance_types\n...\n```\n\nWhen invoking the config-lint, include the -var option, as in this example:\n\n```\nconfig-lint -rules <RULES_FILE> -var \"instance_types=t2.small,c3.medium\" <CONFIG_FILES>\n```\n"
  },
  {
    "path": "docs/yaml.md",
    "content": "# Parsing an Arbitrary YAML file\n\nSet the Type to YAML, and provide a `resources` section to describe how to discover the resources in a YAML file.\nThe other parsers have code that expects a specific file format, and it converts those into a collection of\nresource objects that will be linted.\n\nFor arbitrary YAML files, the resources describes how resources should be loaded. Each resources has these attributes:\n\n* type: identifies the type of resource. Rules with a matching `resource` will be applied to these resources.\n* key: JMESPath expression that should return an array of objects from the YAML file.\n* id: a JMESPATH expression applied to each resource to extract its unique identifier.\n\nThe rules section works the same as in any other linter.\n\nExample:\n\n```\nversion: 1\ndescription: Rules for generic YAML file\ntype: YAML\nfiles:\n  - \"*.config\"\n\n# For generic YAML linting, we need a list of resources\n# Each entry in the list describes the resource type, how to discover it in the file, and how to get its ID\n# The key attribute is a JMESPath expression that should return an array\n\nresources:\n  - type: widget\n    key: widgets[]\n    id: id\n  - type: gadget\n    key: gadgets[]\n    id: name\n  - type: contraption\n    key: other_stuff.contraptions[]\n    id: ids.serial_number\n  # include the root document in a single element array with a literal id\n  - type: document\n    key: '[@]'\n    id: '`\"Document\"`'\n\nrules:\n\n  - id: DOCUMENT_KEYS\n    message: Unexpected document key\n    severity: FAILURE\n    resource: document\n    assertions:\n      - every:\n          key: \"keys(@)\"\n          assertions:\n            - key: \"@\"\n              op: in\n              value: widgets,gadgets,other_stuff\n\n  - id: WIDGET_NAME\n    message: Widget needs a name\n    severity: FAILURE\n    resource: widget\n    assertions:\n      - key: name\n        op: present\n\n  - id: GADGET_COLOR\n    message: Gadget has missing or invalid color\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: color\n        op: in\n        value: red,blue,green\n\n  - id: GADGET_PROPERTIES\n    message: Gadget has missing properties\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: name,color\n\n  - id: CONTRAPTION_SIZE\n    message: Contraption size should be less than 1000\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - key: size\n        op: lt\n        value: 1000\n        value_type: integer\n\n  - id: CONTRAPTION_LOCATIONS\n    message: Contraption location must have city\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - every:\n          key: locations\n          assertions:\n            - key: city\n              op: present\n```\n\nThere is an example file [here](example-files/config/generic.config) that this rule file can scan.\n"
  },
  {
    "path": "example-files/config/cloudfront.tf",
    "content": "resource \"aws_s3_bucket\" \"b\" {\n  bucket = \"mybucket\"\n  acl    = \"private\"\n\n  tags {\n    Name = \"My bucket\"\n  }\n}\n\nresource \"aws_cloudfront_distribution\" \"s3_distribution\" {\n  origin {\n    domain_name = \"${aws_s3_bucket.b.bucket_domain_name}\"\n    origin_id   = \"myS3Origin\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  enabled             = true\n  is_ipv6_enabled     = true\n  comment             = \"Some comment\"\n  default_root_object = \"index.html\"\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"mylogs.s3.amazonaws.com\"\n    prefix          = \"myprefix\"\n  }\n\n  aliases = [\"mysite.example.com\", \"yoursite.example.com\"]\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"myS3Origin\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  price_class = \"PriceClass_200\"\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  tags {\n    Environment = \"production\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}\n"
  },
  {
    "path": "example-files/config/elb.tf",
    "content": "resource \"aws_elb\" \"bar\" {\n  name               = \"foobar-terraform-elb\"\n  availability_zones = [\"us-west-2a\", \"us-west-2b\", \"us-west-2c\"]\n\n  access_logs {\n    bucket        = \"foo\"\n    bucket_prefix = \"bar\"\n    interval      = 60\n  }\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n\n  listener {\n    instance_port      = 8000\n    instance_protocol  = \"http\"\n    lb_port            = 443\n    lb_protocol        = \"https\"\n    ssl_certificate_id = \"arn:aws:iam::123456789012:server-certificate/certName\"\n  }\n\n  health_check {\n    healthy_threshold   = 2\n    unhealthy_threshold = 2\n    timeout             = 3\n    target              = \"HTTP:8000/\"\n    interval            = 30\n  }\n\n  instances                   = [\"${aws_instance.foo.id}\"]\n  cross_zone_load_balancing   = true\n  idle_timeout                = 400\n  connection_draining         = true\n  connection_draining_timeout = 400\n\n  tags {\n    Name = \"foobar-terraform-elb\"\n  }\n}\n\nresource \"aws_elb\" \"elb_no_access_logs\" {\n  name               = \"foobar-terraform-elb\"\n  availability_zones = [\"us-west-2a\", \"us-west-2b\", \"us-west-2c\"]\n\n  listener {\n    instance_port     = 8000\n    instance_protocol = \"http\"\n    lb_port           = 80\n    lb_protocol       = \"http\"\n  }\n\n  listener {\n    instance_port      = 8000\n    instance_protocol  = \"http\"\n    lb_port            = 443\n    lb_protocol        = \"https\"\n    ssl_certificate_id = \"arn:aws:iam::123456789012:server-certificate/certName\"\n  }\n\n  health_check {\n    healthy_threshold   = 2\n    unhealthy_threshold = 2\n    timeout             = 3\n    target              = \"HTTP:8000/\"\n    interval            = 30\n  }\n\n  instances                   = [\"${aws_instance.foo.id}\"]\n  cross_zone_load_balancing   = true\n  idle_timeout                = 400\n  connection_draining         = true\n  connection_draining_timeout = 400\n\n  tags {\n    Name = \"foobar-terraform-elb\"\n  }\n}\n\n"
  },
  {
    "path": "example-files/config/generic.config",
    "content": "#\nwidgets:\n  - id: W1\n    name: Foo\n  - id: W2\n    name: Bar\n  - id: W3\n    key: Baz\n\ngadgets:\n  - name: first_gadget\n    color: red\n  - name: second_gadget\n    color: blue\n  - name: third_gadget\n    color: green\n  - name: fourth_gadget\n    color: yellow\n\nother_stuff:\n  contraptions:\n    - ids:\n        serial_number: S1000\n        sku: S1234\n      size: 10\n      locations:\n        - city: Seattle\n        - city: San Francisco\n    - ids:\n        serial_number: S2000\n        sku: S5678\n      size: 20\n      locations:\n        - city: New York\n    - ids:\n        serial_number: S3000\n        sku: S0101\n      size: 4000\n      locations:\n        - city: Paris\n        - city: Munich\n        - city: Florence\n"
  },
  {
    "path": "example-files/config/iam.tf",
    "content": "resource \"aws_iam_role\" \"iam_role_1\" {\n    name = \"iam_role_1\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_role\" \"iam_role_with_not\" {\n    name = \"iam_role_1\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"NotAction\": \"sts:AssumeRole\",\n      \"NotResource\": [\n        \"resource1\",\n        \"resource2\"\n      ],\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\n\nresource \"aws_iam_role\" \"role_with_invalid_policy\" {\n    name = \"role1\"\n    assume_role_policy = [ \"invalid\" ]\n}\n\nresource \"aws_iam_policy\" \"policy_1\" {\n    name = \"policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_policy\" \"policy_with_not_action\" {\n    name = \"policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"NotAction\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_policy\" \"policy_with_not_resource\" {\n    name = \"policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\",\n      \"NotResource\": [\n        \"resource1\",\n        \"resource2\"\n      ]\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_policy\" \"policy_with_wildcards\" {\n    name = \"policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"*\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\",\n      \"Resource\": \"*\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_group_policy\" \"group_policy_1\" {\n    name = \"group_policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_role_policy\" \"role_policy_1\" {\n    name = \"role_policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n\nresource \"aws_iam_user_policy\" \"user_policy_1\" {\n    name = \"user_policy_1\"\n    policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "example-files/config/my-pod.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: my-pod\nspec:\n  serviceAccountName: build-robot\n  securityContext:\n    readOnlyFilesystem: true\n    runAsNonRoot: true\n"
  },
  {
    "path": "example-files/config/network-policy-1.yml",
    "content": "apiVersion: v1\nkind: NetworkPolicy\nmetadata:\n  name: policy1\nspec:\n  allowIncoming:\n    from:\n      - pods:\n          segment: frontend\n    toPorts:\n      - port: 80\n        protocol: TCP\n  podSelector:\n    segment: backend\n"
  },
  {
    "path": "example-files/config/network-policy-2.yml",
    "content": "apiVersion: v1\nkind: NetworkPolicy\nmetadata:\n  name: policy2\nspec:\n  allowIncoming:\n    toPorts:\n      - port: 80\n        protocol: TCP\n  podSelector:\n    segment: backend\n\n"
  },
  {
    "path": "example-files/config/no-containers.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: random\nspec:\n  not_containers:\n  - name: Random\n    image: hello\n"
  },
  {
    "path": "example-files/config/not-a-pod.yml",
    "content": "apiVersion: v1\nkind: Invalid\nmetadata:\n  name: nginx\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n\n"
  },
  {
    "path": "example-files/config/pod-nginx.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n"
  },
  {
    "path": "example-files/config/pod-redis.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: redis\nspec:\n  containers:\n  - name: redis\n    image: redis\n    volumeMounts:\n    - name: redis-persistent-storage\n      mountPath: /data/redis\n  volumes:\n  - name: redis-persistent-storage\n    emptyDir: {}\n"
  },
  {
    "path": "example-files/config/policy.yml",
    "content": "apiVersion: abac.authorization.kubernetes.io/v1beta1\nkind: Policy\nspec:\n  user: alice\n  namespace: default\n  resource: pods\n  readonly: true\n"
  },
  {
    "path": "example-files/config/provider.tf",
    "content": "provider \"aws\" {\n    region = \"us-east-1\"\n}\n"
  },
  {
    "path": "example-files/config/s3-bucket-policy.tf",
    "content": "resource \"aws_s3_bucket\" \"b\" {\n  bucket = \"a_test_bucket\"\n}\n\nresource \"aws_s3_bucket_policy\" \"b\" {\n  bucket = \"${aws_s3_bucket.b.id}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYBUCKETPOLICY\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": \"user\",\n      \"Action\": \"s3:GetBucket\",\n      \"Resource\": \"arn:aws:s3:::a_test_bucket/*\",\n      \"Condition\": {\n        \"Bool\": {\n          \"aws:SecureTransport\": \"true\"\n        }\n      }\n    }\n  ]\n}\nPOLICY\n}\n"
  },
  {
    "path": "example-files/config/s3-encryption.tf",
    "content": "resource \"aws_kms_key\" \"key_for_s3_encryption\" {\n  description = \"key for S3 bucket encryption\"\n}\n\nresource \"aws_s3_bucket\" \"bucket_example_1\" {\n  acl = \"public-read\"\n  tags = {\n    project = \"web\"\n    classification = \"PII\"\n  }\n}\n\nresource \"aws_s3_bucket\" \"bucket_example_2\" {\n  acl = \"public-read-write\"\n  server_side_encryption_configuration {\n    rule {\n      apply_server_side_encryption_by_default {\n        kms_master_key_id = \"${aws_kms_key.key_for_s3_encryption.arn}\"\n        sse_algorithm     = \"aws:kms\"\n      }\n    }\n  }\n  tags = {\n    project = \"web\"\n    classification = \"HIPAA\"\n  }\n}\n\nresource \"aws_s3_bucket\" \"bucket_example_3\" {\n  tags = {\n    classification = \"public\"\n  }\n}\n"
  },
  {
    "path": "example-files/config/s3.tf",
    "content": "resource \"aws_s3_bucket\" \"bucket_example\" {\n  bucket = \"my-data-lake\"\n}\n\nresource \"aws_s3_bucket\" \"bucket_name_with_underscores\" {\n  bucket = \"my_data_lake\"\n}\n\nresource \"aws_s3_bucket\" \"bucket_name_with_uppercase\" {\n  bucket = \"MYDATALAKE\"\n}\n\nresource \"aws_s3_bucket\" \"b1\" {\n  bucket = \"test-bucket-1\"\n}\n\nresource \"aws_s3_bucket_policy\" \"b1\" {\n  bucket = \"${aws_s3_bucket.b.id}\"\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYBUCKETPOLICY\",\n  \"Statement\": [\n    {\n      \"Sid\": \"IPAllow\",\n      \"Effect\": \"Deny\",\n      \"Principal\": \"*\",\n      \"Action\": \"s3:*\",\n      \"Resource\": \"arn:aws:s3:::my_tf_test_bucket/*\",\n      \"Condition\": {\n         \"IpAddress\": {\"aws:SourceIp\": \"8.8.8.8/32\"}\n      }\n    }\n  ]\n}\nPOLICY\n}\n\nresource \"aws_s3_bucket\" \"b2\" {\n  bucket = \"test-bucket-2\"\n}\n\nresource \"aws_s3_bucket_policy\" \"bucket_with_not\" {\n  bucket = \"${aws_s3_bucket.b.id}\"\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYBUCKETPOLICY\",\n  \"Statement\": [\n    {\n      \"Sid\": \"IPAllow\",\n      \"Effect\": \"Deny\",\n      \"NotPrincipal\": \"*\",\n      \"NotAction\": \"s3:*\",\n      \"Resource\": \"arn:aws:s3:::my_tf_test_bucket/*\",\n      \"Condition\": {\n         \"IpAddress\": {\"aws:SourceIp\": \"8.8.8.8/32\"}\n      }\n    }\n  ]\n}\nPOLICY\n}\n\nresource \"aws_s3_bucket_policy\" \"bucket_with_wildcards\" {\n  bucket = \"${aws_s3_bucket.b.id}\"\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYBUCKETPOLICY\",\n  \"Statement\": [\n    {\n      \"Sid\": \"IPAllow\",\n      \"Effect\": \"Deny\",\n      \"Principal\": \"*\",\n      \"Action\": \"*\",\n      \"Resource\": \"arn:aws:s3:::my_tf_test_bucket/*\",\n      \"Condition\": {\n         \"IpAddress\": {\"aws:SourceIp\": \"8.8.8.8/32\"}\n      }\n    }\n  ]\n}\nPOLICY\n}\n"
  },
  {
    "path": "example-files/config/search-debug.tf",
    "content": "resource \"aws_instance\" \"with_tags\" {\n    ami = \"ami-f2d3638a\"\n    instance_type = \"t2.micro\"\n    tags {\n        \"CostCenter\" = \"1001\"\n        \"Project\" = \"Web\"\n    }\n}\nresource \"aws_instance\" \"without_tags\" {\n    ami = \"ami-f2d3638a\"\n    instance_type = \"m3.medium\"\n}\n"
  },
  {
    "path": "example-files/config/security_group.tf",
    "content": "resource \"aws_security_group\" \"allow_all\" {\n  name        = \"allow_all\"\n  description = \"Allow all inbound traffic\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 0\n    to_port     = 0\n    protocol    = \"-1\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n\n  egress {\n    from_port       = 0\n    to_port         = 0\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n\nresource \"aws_security_group\" \"allow_all_v6\" {\n  name        = \"allow_all\"\n  description = \"Allow all inbound traffic\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 0\n    to_port     = 0\n    protocol    = \"-1\"\n    cidr_blocks = [\"::/0\"]\n  }\n\n  egress {\n    from_port       = 0\n    to_port         = 0\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n\nresource \"aws_security_group\" \"allow_all_for_ssh\" {\n  name        = \"allow_all\"\n  description = \"Allow all inbound traffic\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"-1\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n\n  egress {\n    from_port       = 0\n    to_port         = 0\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n\nresource \"aws_security_group\" \"allow_all_for_rd\" {\n  name        = \"allow_all\"\n  description = \"Allow all inbound traffic\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 3389\n    to_port     = 3389\n    protocol    = \"-1\"\n    cidr_blocks = [\"0.0.0.0/0\"]\n  }\n\n  egress {\n    from_port       = 0\n    to_port         = 0\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n\nresource \"aws_security_group\" \"non_32_ingress\" {\n  name        = \"allow_all\"\n  description = \"Allow all inbound traffic\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 0\n    to_port     = 0\n    protocol    = \"-1\"\n    cidr_blocks = [\"1.2.3.4/24\"]\n  }\n}\n\nresource \"aws_security_group\" \"ingress_range\" {\n  name        = \"ingress_range\"\n  description = \"Allow inbound traffic for range of ports\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 1000\n    to_port     = 2000\n    protocol    = \"-1\"\n    cidr_blocks = [\"1.2.3.4/32\"]\n  }\n\n  egress {\n    from_port       = 0\n    to_port         = 0\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n\nresource \"aws_security_group\" \"egress_range\" {\n  name        = \"egress_range\"\n  description = \"Allow outbound traffic for range of ports\"\n  vpc_id      = \"${aws_vpc.main.id}\"\n\n  ingress {\n    from_port   = 80\n    to_port     = 80\n    protocol    = \"-1\"\n    cidr_blocks = [\"1.2.3.4/32\"]\n  }\n\n  egress {\n    from_port       = 1000\n    to_port         = 2000\n    protocol        = \"-1\"\n    cidr_blocks     = [\"0.0.0.0/0\"]\n    prefix_list_ids = [\"pl-12c4e678\"]\n  }\n}\n"
  },
  {
    "path": "example-files/config/service-account.yml",
    "content": "apiVersion: v1\nkind: ServiceAccount\nmetadata:\n  name: build-robot\n"
  },
  {
    "path": "example-files/config/sns.tf",
    "content": "resource \"aws_sns_topic\" \"test\" {\n  name = \"my-topic-with-policy\"\n}\n\nresource \"aws_sns_topic_policy\" \"default\" {\n  arn = \"${aws_sns_topic.test.arn}\"\n\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYSNSTOPICPOLICY\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": \"*\",\n      \"Action\": \"*\"\n    } \n  ]\n}\nPOLICY\n}\n\nresource \"aws_sns_topic_policy\" \"sns_topic_policy_with_not\" {\n  arn = \"${aws_sns_topic.test.arn}\"\n\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYSNSTOPICPOLICY\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"NotPrincipal\": \"*\",\n      \"NotAction\": \"*\"\n    } \n  ]\n}\nPOLICY\n}\n\n"
  },
  {
    "path": "example-files/config/sqs.tf",
    "content": "resource \"aws_sqs_queue\" \"q\" {\n  name = \"examplequeue\"\n}\n\nresource \"aws_sqs_queue_policy\" \"sqs_policy_1\" {\n  queue_url = \"${aws_sqs_queue.q.id}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"sqspolicy\",\n  \"Statement\": [\n    {\n      \"Sid\": \"First\",\n      \"Effect\": \"Allow\",\n      \"Principal\": \"*\",\n      \"Action\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.q.arn}\",\n      \"Condition\": {\n        \"ArnEquals\": {\n          \"aws:SourceArn\": \"${aws_sqs_queue.q.arn}\"\n        }\n      }\n    }\n  ]\n}\nPOLICY\n}\n\nresource \"aws_sqs_queue_policy\" \"sqs_policy_2\" {\n  queue_url = \"${aws_sqs_queue.q.id}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"sqspolicy\",\n  \"Statement\": [\n    {\n      \"Sid\": \"First\",\n      \"Effect\": \"Allow\",\n      \"NotPrincipal\": \"*\",\n      \"NotAction\": \"sqs:SendMessage\",\n      \"Resource\": \"${aws_sqs_queue.q.arn}\",\n      \"Condition\": {\n        \"ArnEquals\": {\n          \"aws:SourceArn\": \"${aws_sqs_queue.q.arn}\"\n        }\n      }\n    }\n  ]\n}\nPOLICY\n}\n\nresource \"aws_sqs_queue_policy\" \"sqs_policy_3\" {\n  queue_url = \"${aws_sqs_queue.q.id}\"\n\n  policy = <<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"sqspolicy\",\n  \"Statement\": [\n    {\n      \"Sid\": \"First\",\n      \"Effect\": \"Allow\",\n      \"Principal\": \"*\",\n      \"Action\": \"*\",\n      \"Resource\": \"${aws_sqs_queue.q.arn}\",\n      \"Condition\": {\n        \"ArnEquals\": {\n          \"aws:SourceArn\": \"${aws_sqs_queue.q.arn}\"\n        }\n      }\n    }\n  ]\n}\nPOLICY\n}\n"
  },
  {
    "path": "example-files/config/terraform.tf",
    "content": "resource \"aws_instance\" \"first\" {\n    ami = \"ami-f2d3638a\"\n    instance_type = \"t2.micro\"\n    tags {\n        \"CostCenter\" = \"1001\"\n        \"Project\" = \"Web\"\n    }\n}\nresource \"aws_instance\" \"second\" {\n    ami = \"ami-f2d3638a\"\n    instance_type = \"m3.medium\"\n    tags {\n    Department = \"Operations\"\n        CostCenter = \"2001\"\n        \"Project\" = \"Web\"\n    }\n}\nresource \"aws_instance\" \"third\" {\n    ami = \"ami-f2d3638b\"\n    instance_type = \"c4.large\"\n}\nresource \"aws_instance\" \"foo\" {\n    ami = \"ami-f2d3638b\"\n    instance_type = \"c4.large\"\n    tags {\n        Foo = \"Foo\"\n        \"Project\" = \"Web\"\n    }\n}\nresource \"aws_iam_role\" \"role1\" {\n    name = \"role1\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\nresource \"aws_iam_role\" \"role2\" {\n    name = \"non_compliant\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n     {\n        \"Action\": \"*\",\n        \"Principal\": { \"Service\": \"ec2.amazonaws.com\" },\n        \"Effect\": \"Allow\",\n        \"Resources\": \"*\"\n     }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "example-files/config/variables.tf",
    "content": "variable \"instance_type\" {\n  default = \"t2.micro\"\n}\n\nvariable \"ami\" {\n  default = \"ami-f2d3638a\"\n}\n\nvariable \"project\" {\n  default = \"demo\"\n}\n\nresource \"aws_instance\" \"first\" {\n  ami = \"${var.ami}\"\n  instance_type = \"${var.instance_type}\"\n  tags = {\n    project = \"${var.project}\"\n  }\n}\n\nvariable \"instance_type_2\" {\n  default = \"c4.large\"\n}\n\nresource \"aws_instance\" \"second\" {\n  ami = \"${var.ami}\"\n  instance_type = \"${var.instance_type_2}\"\n  tags = {\n    project = \"${var.project}\"\n  }\n}\n\n"
  },
  {
    "path": "example-files/config/volumes.tf",
    "content": "resource \"aws_ebs_volume\" \"vol1\" {\n    availability_zone = \"us-west-2a\"\n    size = 40\n    tags {\n        Name = \"HelloWorld\"\n    }\n    encrypted = true\n}\nresource \"aws_ebs_volume\" \"vol2\" {\n    availability_zone = \"us-west-2a\"\n    size = 40\n    tags {\n        Name = \"HelloWorld\"\n    }\n    encrypted = false\n}\nresource \"aws_ebs_volume\" \"vol3\" {\n    availability_zone = \"us-west-2a\"\n    size = 40\n    tags {\n        Name = \"HelloWorld\"\n    }\n}\n"
  },
  {
    "path": "example-files/config/web-and-helper.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: www\nspec:\n  containers:\n  - name: nginx\n    image: nginx\n    volumeMounts:\n    - mountPath: /srv/www\n      name: www-data\n      readOnly: true\n  - name: git-monitor\n    image: kubernetes/git-monitor\n    env:\n    - name: GIT_REPO\n      value: http://github.com/some/repo.git\n    volumeMounts:\n    - mountPath: /data\n      name: www-data\n  volumes:\n  - name: www-data\n    emptyDir: {}\n"
  },
  {
    "path": "example-files/demo-resources/s3-bucket.tf",
    "content": "resource \"aws_kms_key\" \"test_key\" {\n  enable_key_rotation = true\n}\n\nresource \"aws_s3_bucket\" \"b1\" {\n  bucket = \"test-bucket-1\"\n\n  #   server_side_encryption_configuration {\n  #     rule {\n  #       apply_server_side_encryption_by_default {\n  #         kms_master_key_id = aws_kms_key.test_key.arn\n  #         sse_algorithm     = \"aws:kms\"\n  #       }\n  #     }\n  #   }\n\n  tags = {\n    \"Department\" = \"invalid\"\n  }\n}\n\nresource \"aws_s3_bucket_policy\" \"b1\" {\n  bucket = aws_s3_bucket.b1.id\n\n  policy =<<POLICY\n{\n  \"Version\": \"2012-10-17\",\n  \"Id\": \"MYBUCKETPOLICY\",\n  \"Statement\": [\n    {\n      \"Sid\": \"IPAllow\",\n      \"Effect\": \"Deny\",\n      \"Principal\": \"*\",\n      \"Action\": \"s3:*\",\n      \"Resource\": \"arn:aws:s3:::my_tf_test_bucket/*\",\n      \"Condition\": {\n         \"IpAddress\": {\"aws:SourceIp\": \"8.8.8.8/32\"}\n      }\n    }\n  ]\n}\nPOLICY\n}\n"
  },
  {
    "path": "example-files/rules/alias.yml",
    "content": "version: 1\ndescription: Rules for generic YAML file\ntype: YAML\nfiles:\n  - \"*.config\"\n\n# some anchors to keep rules DRY\n\nhas_name: &has_name\n  key: name\n  op: present\n\nhas_name: &has_description\n  key: description\n  op: present\n\nhas_name_and_description: &has_name_and_description\n  and:\n    - <<: *has_name\n    - <<: *has_description\n\n# resources to find in the config file\n\nresources:\n  - type: widget\n    key: widgets[]\n    id: id\n  - type: gadget\n    key: gadgets[]\n    id: name\n\n# rules to apply\n\nrules:\n\n  - id: WIDGET_PROPERTIES\n    message: Widget needs name and description\n    severity: FAILURE\n    resource: widget\n    assertions:\n      - <<: *has_name_and_description\n\n  - id: GADGET_PROPERTIES\n    message: Gadget needs name a description\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - <<: *has_name_and_description\n\n"
  },
  {
    "path": "example-files/rules/generic-json.yml",
    "content": "version: 1\ndescription: Rules for users in a JSON file\ntype: JSON\n\nresources:\n  - type: User\n    key: Users\n    id: UserName\n\nrules:\n  - id: DEPARTMENT_REQUIRED\n    message: User must have a department\n    resource: User\n    assertions:\n      - key: Department\n        op: present\n"
  },
  {
    "path": "example-files/rules/generic-yaml.yml",
    "content": "version: 1\ndescription: Rules for generic YAML file\ntype: YAML\nfiles:\n  - \"*.config\"\n\n# For generic YAML linting, we need a list of resources\n# Each entry in the list describes the resource type, how to discover it in the file, and how to get its ID\n# The key attribute is a JMESPath expression that should return an array\n\nresources:\n  - type: widget\n    key: widgets[]\n    id: id\n  - type: gadget\n    key: gadgets[]\n    id: name\n  - type: contraption\n    key: other_stuff.contraptions[]\n    id: ids.serial_number\n  # include the root document in a single element array with a literal id\n  - type: document\n    key: '[@]'\n    id: '`\"Document\"`'\n\nrules:\n\n  - id: DOCUMENT_KEYS\n    message: Unexpected document key\n    severity: FAILURE\n    resource: document\n    assertions:\n      - every:\n          key: \"keys(@)\"\n          expressions:\n            - key: \"@\"\n              op: in\n              value: widgets,gadgets,other_stuff\n\n  - id: WIDGET_NAME\n    message: Widget needs a name\n    severity: FAILURE\n    resource: widget\n    assertions:\n      - key: name\n        op: present\n\n  - id: GADGET_COLOR\n    message: Gadget has missing or invalid color\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: color\n        op: in\n        value: red,blue,green\n\n  - id: GADGET_PROPERTIES\n    message: Gadget has missing properties\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: name,color\n\n  - id: CONTRAPTION_SIZE\n    message: Contraption size should be less than 1000\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - key: size\n        op: lt\n        value: 1000\n        value_type: integer\n\n  - id: CONTRAPTION_LOCATIONS\n    message: Contraption location must have city\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - every:\n          key: locations\n          expressions:\n            - key: city\n              op: present\n\n"
  },
  {
    "path": "example-files/rules/iam-policies.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules for demo\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  \n  - id: NO_IAM_ROLES\n    message: Creating an IAM role is not allowed\n    resource: \"aws_iam_role\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_ROLE_POLICIES\n    message: Creating an IAM role policy is not allowed\n    resource: \"aws_iam_role_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_GROUPS\n    message: Creating an IAM group is not allowed\n    resource: \"aws_iam_group\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_GROUP_POLICIES\n    message: Creating an IAM group policy is not allowed\n    resource: \"aws_iam_group_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_POLICIES\n    message: Creating an IAM policy is not allowed\n    resource: \"aws_iam_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_USERS\n    message: Creating an IAM user is not allowed\n    resource: \"aws_iam_user\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_USER_POLICIES\n    message: Creating an IAM user policy is not allowed\n    resource: \"aws_iam_user_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_INSTANCE_PROFILE\n    message: Creating an IAM instance profile is not allowed\n    resource: \"aws_iam_instance_profile\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n"
  },
  {
    "path": "example-files/rules/iam-restricted.yml",
    "content": "---\nversion: 1\ndescription: Terraform rules for creation of IAM resources\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  \n  - id: NO_IAM_ROLES\n    message: Creating an IAM role is not allowed\n    resource: \"aws_iam_role\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_ROLE_POLICIES\n    message: Creating an IAM role policy is not allowed\n    resource: \"aws_iam_role_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_GROUPS\n    message: Creating an IAM group is not allowed\n    resource: \"aws_iam_group\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_GROUP_POLICIES\n    message: Creating an IAM group policy is not allowed\n    resource: \"aws_iam_group_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_POLICIES\n    message: Creating an IAM policy is not allowed\n    resource: \"aws_iam_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_USERS\n    message: Creating an IAM user is not allowed\n    resource: \"aws_iam_user\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n  - id: NO_IAM_USER_POLICIES\n    message: Creating an IAM user policy is not allowed\n    resource: \"aws_iam_user_policy\"\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: absent\n\n\n"
  },
  {
    "path": "example-files/rules/kubernetes.yml",
    "content": "version: 1\ndescription: Rules for Kubernetes spec files\ntype: Kubernetes\nfiles:\n  - \"*.yml\"\nrules:\n\n  - id: ALLOW_KIND\n    severity: FAILURE\n    message: Allowed kinds\n    resource: \"*\"\n    assertions:\n      - key: kind\n        op: in\n        value: Pod,Policy,ServiceAccount,NetworkPolicy\n    tags:\n      - kind\n\n  - id: POD_CONTAINERS\n    severity: FAILURE\n    message: Pod must include containers\n    resource: Pod\n    assertions:\n      - key: spec.containers\n        op: present\n    tags:\n      - pod\n\n  - id: POD_SERVICE_ACCOUNT\n    severity: FAILURE\n    message: Pod should use a service account\n    resource: Pod\n    assertions:\n      - key: serviceAccountName\n        op: present\n    tags:\n      - pod\n\n  - id: POD_SECURITY_CONTEXT\n    severity: FAILURE\n    message: Pod should set securityContent\n    resource: Pod\n    assertions:\n      - key: spec.securityContext.runAsNonRoot\n        op: eq\n        value: true\n      - key: spec.securityContext.readOnlyRootFilesystem\n        op: eq\n        value: true\n    tags:\n      - pod\n      - security\n\n  - id: POLICY\n    severity: FAILURE\n    message: Policy must include a spec\n    resource: Policy\n    assertions:\n      - key: spec\n        op: present\n    tags:\n      - policy\n\n  - id: DEFAULT_NAMESPACE\n    severity: FAILURE\n    message: Policy should not use default namespace\n    resource: Policy\n    assertions:\n      - key: spec.namespace\n        op: ne\n        value: default\n    tags:\n      - policy\n\n  - id: CUSTOM\n    severity: FAILURE\n    message: Custom\n    resource: Policy\n    invoke:\n      url: https://19kfojjbi2.execute-api.us-east-1.amazonaws.com/dev/failure\n      payload: \"{ user: spec.user, namespace: spec.namespace }\"\n    tags:\n      - custom\n\n  - id: NETWORK\n    severity: FAILURE\n    message: Network policy should include from pods\n    resource: NetworkPolicy\n    assertions:\n      - key: spec.allowIncoming.from[].pods\n        op: present\n    tags:\n      - network\n"
  },
  {
    "path": "example-files/rules/lint-rules-with-error.yml",
    "content": "version: 1\ndescription: Rules for config-lint with an error\ntype: LintRules\nfiles:\n  - \"*.yml\"\nrules:\n\n  - id: VALID_TYPE\n    message: This has a single assertion with both and, or keywords, which is an error\n    resource: LintRuleSet\n    severity: FAILURE\n    assertions:\n      - and:\n        - key: type\n          op: in\n          value: Terraform,Kubernetes,SecurityGroup,IAMUser,AWSConfig,LintRules,YAML\n        - key: rules\n          op: not-empty\n        or:\n        - key: version\n          op: eq\n          value: 1\n        - key: version\n          op: eq\n          value: 2\n"
  },
  {
    "path": "example-files/rules/no-iam-actions.yml",
    "content": "version: 1\ndescription: Do not allow IAM actions\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: NO_IAM_ACTIONS\n    message: IAM roles should not allow IAM actions\n    resource: aws_iam_role\n    assertions:\n      - key: assume_role_policy.Statement[].Action[]\n        op: not-contains\n        value: \"iam:*\"\n    severity: FAILURE\n    tags:\n      - iam\n"
  },
  {
    "path": "example-files/rules/s3-encryption.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: S3_BUCKET_ENCRYPTION\n    message: S3 Bucket should be encrypted\n    resource: aws_s3_bucket\n    severity: FAILURE\n    conditions:\n      - key: \"tags[].classification[] | [0]\"\n        op: in\n        value: PII,HIPAA\n    assertions:\n      - key: server_side_encryption_configuration\n        op: present\n\n"
  },
  {
    "path": "example-files/rules/terraform-more.yml",
    "content": "version: 1\ndescription: More rules for terraform\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: ALB_LISTENER\n    message: ALB listener security checks\n    resource: aws_alb_listener\n    severity: FAILURE\n    assertions:\n      - key: port\n        op: eq\n        value: 443\n      - key: protocol\n        op: ne\n        value: http\n        options: ignore-case\n      - key: ssl_policy\n        op: present\n      - key: ssl_policy\n        op: ne\n        value: ELBSecurityPolicy-2015-05\n      - key: ssl_policy\n        op: ne\n        value: ELBSecurityPolicy-TLS-1-0-2015-04\n      - key: certificate_arn\n        op: present\n      - key: access_logs\n        op: present\n      - key: internal  # TODO if the default is true, then add an or that checks absent or is-true\n        op: is-true\n\n  - id: AMI\n    message: AMI security checks\n    resource: aws_ami\n    severity: FAILURE\n    assertions:\n      - key: ebs_block_device.encrypted\n        op: is-true\n      - key: ebs_block_device.kms_key_id\n        op: present\n        \n  - id: AMI_COPY\n    message: AMI security checks\n    resource: aws_ami_copy\n    severity: FAILURE\n    assertions:\n      - key: ebs_block_device.encrypted\n        op: is-true\n      - key: ebs_block_device.kms_key_id\n        op: present\n\n  - id: API_GATEWAY_DOMAIN_NAME\n    message: API Gateway checks\n    resource: aws_api_gateway_domain_name\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: certificate_name,certificate_body,certificate_change,certificate_private_key\n\n  - id: INSTANCE\n    message: Instance checks\n    resource: aws_instance\n    severity: FAILURE\n    assertions:\n      - or:\n          - key: ebs_block_device\n            op: absent\n          - key: ebs_block_device.encrypted\n            op: is-true\n        \n  - id: CLOUDFRONT_DISTRIBUTION\n    message: CloudFront Distribution checks\n    resource: aws_cloudfront_distribution\n    severity: FAILURE\n    assertions:\n      - or:\n        - key: origin.custom_origin_policy\n          op: absent\n        - key: origin.custom_origin_policy.origin_protocol_policy\n          op: eq\n          value: https-only\n      - key: default_cache_behavior.viewer_protocol_policy\n        op: ne\n        value: allow-all\n      - key: cache_behavior.viewer_protocol_policy\n        op: ne\n        value: allow-all\n      - key: logging_config\n        op: present\n\n  - id: CLOUDTRAIL\n    message: CloudTrail checks\n    resource: aws_cloudtrail\n    severity: FAILURE\n    assertions:\n      - key: kms_key_id\n        op: present\n\n  - id: CODEBUILD_PROJECT\n    message: CodeBuild Project checks\n    resource: aws_codebuild_project\n    severity: FAILURE\n    assertions:\n      - key: encryption_key\n        op: present\n\n  - id: CODEPIPELINE\n    message: CodePipeline checks\n    resource: aws_codepipeline\n    severity: FAILURE\n    assertions:\n      - key: encryption_key\n        op: present\n\n  - id: DB_INSTANCE\n    message: DB Instance checks\n    resource: aws_db_instance\n    severity: FAILURE\n    assertions:\n      - key: storage_encrypted\n        op: is-true\n      - key: kms_key_id\n        op: present\n\n  - id: ELB\n    message: ELB checks\n    resource: aws_elb\n    severity: FAILURE\n    assertions:\n      - key: access_logs\n        op: present\n      - key: listener.lb_port\n        op: not-in\n        value: 80,21,23,5900\n\n  - id: DMS_REPLICATION_INSTANCE\n    message: DMS Replication Instance checks\n    resource: aws_dms_replication_instance\n    severity: FAILURE\n    assertions: \n      - key: kms_key_arn\n        op: present\n\n  - id: EBS_VOLUME\n    message: EBS Volume checks\n    resource: aws_ebs_volume\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n      - key: kms_key_id\n        op: present\n\n  - id: EFS\n    message: EFS Checks\n    resource: aws_efs_file_system\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n      - key: kms_key_id\n        op: present\n \n  - id: ELASTICTRANSCODE_PIPELINE\n    message: ElasticTranscoderPipeline checks\n    resource: aws_elastictranscoder_pipeline\n    severity: FAILURE\n    assertions:\n      - key: aws_kms_key_arn\n        op: present\n\n  - id: KINESIS_FIREHOSE_DELIVERY_STREAM\n    message: KinesisFirehoseDeliveryStream checks \n    resource: aws_kinesis_firehose_delivery_stream\n    severity: FAILURE\n    assertions:\n      - or:\n        - key: s3_configuration.kms_key_arn\n          op: present\n        - key: extended_s3_configuration.kms_key_arn\n          op: present\n\n  - id: LAMBDA_FUNCTION\n    message: Lambda checks\n    resource: aws_lambda_function\n    severity: FAILURE\n    assertions:\n      - key: kms_key_arn\n        op: present\n\n  - id: OPSWORKS_APPLICATION\n    message: OpsWorks Application checks\n    resource: aws_opsworks_application\n    severity: FAILURE\n    assertions:\n      - key: enable_ssl\n        op: is-true\n\n  - id: RDS_CLUSTER\n    message: RDSCluster checks\n    resource: aws_rds_cluster\n    severity: FAILURE\n    assertions:\n      - key: storage_encrypted\n        op: is-true\n      - key: kms_key_id\n        op: present\n\n  - id: REDSHIFT_CLUSTER\n    message: RedshiftCluster checks\n    resource: aws_redshift_cluster\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: is-true\n      - key: kms_key_id\n        op: present\n\n  - id: S3_BUCKET_OBJECT\n    message: S3 Bucket Object checks\n    resource: aws_s3_bucket_object\n    severity: FAILURE\n    assertions:\n      - key: kms_key_id\n        op: present\n\n  - id: SQS_QUEUE\n    message: SQSQueue checks    \n    resource: aws_sqs_queue\n    severity: FAILURE\n    assertions:\n      - key: \"@\"\n        op: has-properties \n        value: kms_master_key_id,kms_data_key_reuse_period_seconds\n"
  },
  {
    "path": "example-files/rules/terraform.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: R1\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n    tags:\n      - ec2\n\n  - id: R2\n    message: Not an approved AMI\n    resource: aws_instance\n    assertions:\n      - key: ami\n        op: in\n        value: ami-f2d3638a\n    severity: FAILURE\n    tags:\n      - ec2\n\n  - id: R3\n    message: Department tag is not valid\n    resource: aws_instance\n    except:\n        - foo\n        - third\n    assertions:\n      - key: \"tags[].Department | [0]\"\n        op: in\n        value: Operations,Sales,Marketing,Engineering\n    severity: WARNING\n    tags:\n      - ec2\n\n  - id: R4\n    message: CostCenter tag is missing\n    resource: aws_instance\n    assertions:\n      - key: \"tags[].CostCenter | [0]\"\n        op: present\n    severity: FAILURE\n\n  - id: ORTEST\n    resource: aws_instance\n    message: Should have instance_type of t2.micro or m3.medium\n    severity: WARNING\n    assertions:\n      - or:\n        - key: instance_type\n          op: eq\n          value: t2.micro\n        - key: instance_type\n          op: eq\n          value: m3.medium\n\n  - id: ANDTEST\n    resource: aws_instance\n    message: Should have both Project and Department tags\n    severity: WARNING\n    assertions:\n      - and:\n        - key: \"tags[].Department | [0]\"\n          op: present\n        - key: \"tags[].Project | [0]\"\n          op: present\n    tags:\n      - and-test\n\n  - id: NOTTEST\n    resource: aws_instance\n    message: Should not have instance type of c4.large\n    severity: WARNING\n    assertions:\n      - not:\n        - key: instance_type\n          op: eq\n          value: c4.large\n\n  - id: EBS_ENCRYPTION\n    resource: aws_ebs_volume\n    message: EBS Volumes must be encrypted\n    severity: FAILURE\n    assertions:\n      - key: encrypted\n        op: eq\n        value: true\n    tags:\n      - ebs\n\n  - id: SG_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from 0.0.0.0/0 or ::/0\n    severity: WARNING\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[].cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n\n  - id: SG_WORLD_EGRESS\n    resource: aws_security_group\n    message: Security group should not allow egress to 0.0.0.0/0 or ::/0\n    severity: WARNING\n    assertions:\n      - not:\n        - or:\n          - key: \"egress[].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"egress[].cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n\n\n  - id: SG_SSH_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from 0.0.0.0/0 or ::/0\n    severity: FAILURE\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[?(from_port==`22`)].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port==`22`)].cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n\n  - id: SG_RD_WORLD_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from 0.0.0.0/0 or ::/0\n    severity: FAILURE\n    assertions:\n      - not:\n        - or:\n          - key: \"ingress[?(from_port==`3389`)].cidr_blocks[]\"\n            op: contains\n            value: \"0.0.0.0/0\"\n          - key: \"ingress[?(from_port==`3389`)].cidr_blocks[]\"\n            op: contains\n            value: \"::/0\"\n    tags:\n      - sg\n\n  - id: SG_NON_32_INGRESS\n    resource: aws_security_group\n    message: Security group should not allow ingress from CIDR block that does not end in /32\n    severity: WARNING\n    assertions:\n      - key: \"ingress[].cidr_blocks[] | [0]\"\n        op: regex\n        value: \".*/32$\"\n    tags:\n      - sg\n\n  - id: SG_INGRESS_PORT_RANGE\n    resource: aws_security_group\n    message: Security group ingress should specify single port instead of range\n    severity: WARNING\n    assertions:\n      - key: \"ingress[?(from_port!=to_port)]\"\n        op: empty\n    tags:\n      - sg\n\n  - id: SG_EGRESS_PORT_RANGE\n    resource: aws_security_group\n    message: Security group egress should specify single port instead of range\n    severity: WARNING\n    assertions:\n      - key: \"egress[?(from_port!=to_port)]\"\n        op: empty\n    tags:\n      - sg\n\n  - id: SG_MISSING_EGRESS\n    resource: aws_security_group\n    message: Security group should specify egress rules\n    severity: WARNING\n    assertions:\n      - key: \"egress\"\n        op: present\n    tags:\n      - sg\n\n  - id: VALUE_FROM_S3\n    message: Instance type should be in list from S3 object\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          url: s3://config-rules-for-lambda/instance-types\n    severity: WARNING\n    tags:\n      - ec2\n\n  - id: VALUE_FROM_HTTPS\n    message: Instance type should be in list from https endpoint\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value_from:\n          url: https://19kfojjbi2.execute-api.us-east-1.amazonaws.com/dev/instance_types\n    severity: WARNING\n    tags:\n      - ec2\n\n  - id: CLOUDFRONT_LOGGING\n    message: CloudFront Distribution must configure logging\n    resource: s3_distribution\n    severity: FAILURE\n    assertions:\n      - key: logging_config\n        op: present\n\n  - id: POLICY_NOT_ACTION\n    message: Should not use NotAction in IAM policy\n    resource: aws_iam_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotAction\n        op: absent\n    tags:\n      - iam\n\n  - id: POLICY_NOT_RESOURCE\n    message: Should not use NotResource in IAM policy\n    resource: aws_iam_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotResource\n        op: absent\n    tags:\n      - iam\n\n  - id: POLICY_WILDCARD_ACTION\n    message: Should not use wildcard action in IAM policy\n    resource: aws_iam_policy\n    severity: FAILURE\n    assertions:\n      - not:\n        - key: policy.Statement[].Action\n          op: contains\n          value: \"*\"\n    tags:\n      - iam\n\n  - id: POLICY_WILDCARD_RESOURCE\n    message: Should not use wildcard resource in IAM policy\n    resource: aws_iam_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Resource\n          op: contains\n          value: \"*\"\n    tags:\n      - iam\n\n  - id: ROLE_NOT_ACTION\n    message: Should not use NotAction in IAM role\n    resource: aws_iam_role\n    severity: WARNING\n    assertions:\n      - key: assume_role_policy.Statement[].NotAction\n        op: absent\n    tags:\n      - iam\n\n  - id: ROLE_NOT_RESOURCE\n    message: Should not use NotResource in IAM role\n    resource: aws_iam_role\n    severity: WARNING\n    assertions:\n      - key: assume_role_policy.Statement[].NotResource\n        op: absent\n    tags:\n      - iam\n\n  - id: ELB_ACCESS_LOGGING\n    message: ELB should enable access logging\n    resource: aws_elb\n    severity: WARNING\n    assertions:\n      - key: access_logs\n        op: present\n    tags:\n      - elb\n\n  - id: S3_BUCKET_NAME\n    message: Bucket name is invalid\n    resource: aws_s3_bucket\n    assertions:\n      - key: bucket\n        op: regex\n        value: \"^[a-z0-9.-]+$\"\n    severity: FAILURE\n    tags:\n      - s3\n\n  - id: S3_NOT_ACTION\n    message: SHould not use NotAction in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotAction\n        op: absent\n    tags:\n      - s3\n\n  - id: S3_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotPrincipal\n        op: absent\n    tags:\n      - s3\n\n  - id: S3_BUCKET_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard principal in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Principal\n          op: contains\n          value: \"*\"\n    tags:\n      - s3\n\n  - id: S3_BUCKET_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard Principal in S3 bucket policy\n    resource: aws_s3_bucket_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Action\n          op: contains\n          value: \"*\"\n    tags:\n      - s3\n\n  - id: SNS_TOPIC_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard Principal in SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Principal\n          op: contains\n          value: \"*\"\n    tags:\n      - sns\n\n  - id: SNS_TOPIC_POLICY_NOT_ACTION\n    message: Should not use NotAction in SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotAction\n        op: absent\n    tags:\n      - sns\n\n  - id: SNS_TOPIC_POLICY_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in SNS topic policy\n    resource: aws_sns_topic_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotPrincipal\n        op: absent\n    tags:\n      - sns\n\n  - id: SQS_QUEUE_POLICY_WILDCARD_PRINCIPAL\n    message: Should not use wildcard principal in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Principal\n          op: contains\n          value: \"*\"\n    tags:\n      - sqs\n\n  - id: SQS_QUEUE_POLICY_WILDCARD_ACTION\n    message: Should not use wildcard action in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - not:\n        - key: policy.Statement[].Action\n          op: contains\n          value: \"*\"\n    tags:\n      - sqs\n\n  - id: SQS_QUEUE_POLICY_NOT_ACTION\n    message: Should not use NotAction in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotAction\n        op: absent\n    tags:\n      - sqs\n\n  - id: SQS_QUEUE_POLICY_NOT_PRINCIPAL\n    message: Should not use NotPrincipal in SQS queue policy\n    resource: aws_sqs_queue_policy\n    severity: WARNING\n    assertions:\n      - key: policy.Statement[].NotPrincipal\n        op: absent\n    tags:\n      - sqs\n"
  },
  {
    "path": "example-files/rules/variables.tf",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: INSTANCE_TYPE\n    message: Instance type should be t2.micro or m3.medium\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n\n  - id: PROJECT_TAG\n    message: Check project tag\n    resource: aws_instance\n    assertions:\n      - key: \"tags[].project|[0]\"\n        op: eq\n        value: demo\n    severity: WARNING\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/stelligent/config-lint\n\ngo 1.12\n\nrequire (\n\tgithub.com/Azure/go-autorest v12.2.0+incompatible // manually added to fix multiple modules error from goreleaser\n\tgithub.com/aws/aws-sdk-go v1.25.3\n\tgithub.com/bmatcuk/doublestar v1.2.2 // indirect\n\tgithub.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 // indirect\n\tgithub.com/ghodss/yaml v1.0.0\n\tgithub.com/gobuffalo/envy v1.9.0 // indirect\n\tgithub.com/gobuffalo/packd v1.0.0\n\tgithub.com/gobuffalo/packr v1.30.1\n\tgithub.com/gobuffalo/packr/v2 v2.8.0 // indirect\n\tgithub.com/hashicorp/hcl v1.0.0\n\tgithub.com/hashicorp/hcl/v2 v2.2.0\n\tgithub.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590\n\tgithub.com/hashicorp/terraform v0.12.18\n\tgithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af\n\tgithub.com/spf13/pflag v1.0.5 // indirect\n\tgithub.com/stretchr/testify v1.5.1\n\tgithub.com/zclconf/go-cty v1.1.1\n\tgolang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 // indirect\n\tgolang.org/x/lint v0.0.0-20200302205851-738671d3881b // indirect\n\tgolang.org/x/sys v0.0.0-20200217220822-9197077df867 // indirect\n\tgolang.org/x/tools v0.0.0-20200406213809-066fd1390ee0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=\ncloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=\ncloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=\ncloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=\ncloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=\ncloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=\ncloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=\ngithub.com/Azure/azure-sdk-for-go v35.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go v36.2.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/go-autorest v12.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=\ngithub.com/Azure/go-autorest/autorest v0.9.2/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=\ngithub.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=\ngithub.com/Azure/go-autorest/autorest/adal v0.8.1-0.20191028180845-3492b2aff503/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.2.0/go.mod h1:WWTbGPvkAg3I4ms2j2s+Zr5xCGwGqTQh+6M2ZqOczkE=\ngithub.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=\ngithub.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=\ngithub.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=\ngithub.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=\ngithub.com/Azure/go-autorest/autorest/to v0.3.0/go.mod h1:MgwOyqaIuKdG4TL/2ywSsIWKAfJfgHDo8ObuUk3t5sA=\ngithub.com/Azure/go-autorest/autorest/validation v0.2.0/go.mod h1:3EEqHnBxQGHXRYq3HT1WyXAvT7LLY3tl70hw6tQIbjI=\ngithub.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=\ngithub.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=\ngithub.com/Azure/go-ntlmssp v0.0.0-20180810175552-4a21cbd618b4/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=\ngithub.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=\ngithub.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=\ngithub.com/ChrisTrenkamp/goxpath v0.0.0-20170922090931-c385f95c6022/go.mod h1:nuWgzSkT5PnyOd+272uUmV0dnAnAn42Mk7PiQC5VzN4=\ngithub.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=\ngithub.com/Unknwon/com v0.0.0-20151008135407-28b053d5a292/go.mod h1:KYCjqMOeHpNuTOiFQU6WEcTG7poCJrUs0YgyHNtn1no=\ngithub.com/abdullin/seq v0.0.0-20160510034733-d5467c17e7af/go.mod h1:5Jv4cbFiHJMsVxt52+i0Ha45fjshj6wxYr1r19tB9bw=\ngithub.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=\ngithub.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE=\ngithub.com/agext/levenshtein v1.2.2/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=\ngithub.com/agl/ed25519 v0.0.0-20150830182803-278e1ec8e8a6/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=\ngithub.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=\ngithub.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=\ngithub.com/aliyun/alibaba-cloud-sdk-go v0.0.0-20190329064014-6e358769c32a/go.mod h1:T9M45xf79ahXVelWoOBmH0y4aC1t5kXO5BxwyakgIGA=\ngithub.com/aliyun/aliyun-oss-go-sdk v0.0.0-20190103054945-8205d1f41e70/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=\ngithub.com/aliyun/aliyun-tablestore-go-sdk v4.1.2+incompatible/go.mod h1:LDQHRZylxvcg8H7wBIDfvO5g/cy4/sz1iucBlc2l3Jw=\ngithub.com/antchfx/xpath v0.0.0-20190129040759-c8489ed3251e/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk=\ngithub.com/antchfx/xquery v0.0.0-20180515051857-ad5b8c7a47b0/go.mod h1:LzD22aAzDP8/dyiCKFp31He4m2GPjl0AFyzDtZzUu9M=\ngithub.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U=\ngithub.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc=\ngithub.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0 h1:MzVXffFUye+ZcSR6opIgz9Co7WcDx6ZcY+RjfFHoA0I=\ngithub.com/apparentlymart/go-dump v0.0.0-20190214190832-042adf3cf4a0/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=\ngithub.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=\ngithub.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=\ngithub.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=\ngithub.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=\ngithub.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=\ngithub.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=\ngithub.com/aws/aws-sdk-go v1.15.78/go.mod h1:E3/ieXAlvM0XWO57iftYVDLLvQ824smPP3ATZkfNZeM=\ngithub.com/aws/aws-sdk-go v1.25.3 h1:uM16hIw9BotjZKMZlX05SN2EFtaWfi/NonPKIARiBLQ=\ngithub.com/aws/aws-sdk-go v1.25.3/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=\ngithub.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=\ngithub.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=\ngithub.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=\ngithub.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=\ngithub.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/bmatcuk/doublestar v1.1.5/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=\ngithub.com/bmatcuk/doublestar v1.2.2 h1:oC24CykoSAB8zd7XgruHo33E0cHJf/WhQA/7BeXj+x0=\ngithub.com/bmatcuk/doublestar v1.2.2/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=\ngithub.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps=\ngithub.com/bsm/go-vlq v0.0.0-20150828105119-ec6e8d4f5f4e/go.mod h1:N+BjUcTjSxc2mtRGSCPsat1kze3CUtvJN3/jTXlp29k=\ngithub.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=\ngithub.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=\ngithub.com/coreos/bbolt v1.3.0/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=\ngithub.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=\ngithub.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=\ngithub.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=\ngithub.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=\ngithub.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=\ngithub.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=\ngithub.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=\ngithub.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8=\ngithub.com/dnaeon/go-vcr v0.0.0-20180920040454-5637cf3d8a31/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=\ngithub.com/dylanmei/iso8601 v0.1.0/go.mod h1:w9KhXSgIyROl1DefbMYIE7UVSIvELTbMrCfx+QkYnoQ=\ngithub.com/dylanmei/winrmtest v0.0.0-20190225150635-99b7fe2fddf1/go.mod h1:lcy9/2gH1jn/VCLouHA6tOEwLoNVd4GW6zhuKLmHC2Y=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=\ngithub.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835 h1:roDmqJ4Qes7hrDOsWsMCce0vQHz3xiMPjJ9m4c2eeNs=\ngithub.com/fzipp/gocyclo v0.0.0-20150627053110-6acd4345c835/go.mod h1:BjL/N0+C+j9uNX+1xcNuM9vdSIcXCZrQZUYbXOFbgN8=\ngithub.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=\ngithub.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=\ngithub.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=\ngithub.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=\ngithub.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=\ngithub.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=\ngithub.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=\ngithub.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=\ngithub.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=\ngithub.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=\ngithub.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=\ngithub.com/gobuffalo/envy v1.7.1/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=\ngithub.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=\ngithub.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=\ngithub.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4=\ngithub.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=\ngithub.com/gobuffalo/logger v1.0.1 h1:ZEgyRGgAm4ZAhAO45YXMs5Fp+bzGLESFewzAVBMKuTg=\ngithub.com/gobuffalo/logger v1.0.1/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=\ngithub.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc=\ngithub.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM=\ngithub.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=\ngithub.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=\ngithub.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=\ngithub.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=\ngithub.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=\ngithub.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=\ngithub.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4=\ngithub.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=\ngithub.com/gobuffalo/packr/v2 v2.7.1 h1:n3CIW5T17T8v4GGK5sWXLVWJhCz7b5aNLSxW6gYim4o=\ngithub.com/gobuffalo/packr/v2 v2.7.1/go.mod h1:qYEvAazPaVxy7Y7KR0W8qYEE+RymX74kETFqjFoFlOc=\ngithub.com/gobuffalo/packr/v2 v2.8.0 h1:IULGd15bQL59ijXLxEvA5wlMxsmx/ZkQv9T282zNVIY=\ngithub.com/gobuffalo/packr/v2 v2.8.0/go.mod h1:PDk2k3vGevNE3SwVyVRgQCCXETC9SaONCNSXT1Q8M1g=\ngithub.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=\ngithub.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=\ngithub.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=\ngithub.com/golang/groupcache v0.0.0-20180513044358-24b0969c4cb7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=\ngithub.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=\ngithub.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=\ngithub.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=\ngithub.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=\ngithub.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=\ngithub.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=\ngithub.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=\ngithub.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=\ngithub.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=\ngithub.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=\ngithub.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=\ngithub.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=\ngithub.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=\ngithub.com/gophercloud/gophercloud v0.0.0-20190208042652-bc37892e1968/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=\ngithub.com/gophercloud/utils v0.0.0-20190128072930-fbb6ab446f01/go.mod h1:wjDF8z83zTeg5eMLml5EBSlAhbF7G8DobyI1YsMuyzw=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=\ngithub.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=\ngithub.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=\ngithub.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=\ngithub.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=\ngithub.com/hashicorp/aws-sdk-go-base v0.4.0/go.mod h1:eRhlz3c4nhqxFZJAahJEFL7gh6Jyj5rQmQc7F9eHFyQ=\ngithub.com/hashicorp/consul v0.0.0-20171026175957-610f3c86a089/go.mod h1:mFrjN1mfidgJfYP1xrJCF+AfRhr6Eaqhb2+sfyn/OOI=\ngithub.com/hashicorp/errwrap v0.0.0-20180715044906-d6c0cd880357/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=\ngithub.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-azure-helpers v0.10.0/go.mod h1:YuAtHxm2v74s+IjQwUG88dHBJPd5jL+cXr5BGVzSKhE=\ngithub.com/hashicorp/go-checkpoint v0.5.0/go.mod h1:7nfLNL10NsxqO4iWuW6tWW0HjZuDrwkBuEQsVcpCOgg=\ngithub.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=\ngithub.com/hashicorp/go-getter v1.4.0/go.mod h1:7qxyCd8rBfcShwsvxgIguu4KbS3l8bUCwg2Umn7RjeY=\ngithub.com/hashicorp/go-hclog v0.0.0-20180709165350-ff2cf002a8dd/go.mod h1:9bjs9uLqI8l75knNv3lV1kA55veR+WUPSiKIWcQHudI=\ngithub.com/hashicorp/go-hclog v0.0.0-20181001195459-61d530d6c27f/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=\ngithub.com/hashicorp/go-immutable-radix v0.0.0-20180129170900-7f3cd4390caa/go.mod h1:6ij3Z20p+OhOkCSrA0gImAWoHYQRGbnlcuk6XYTiaRw=\ngithub.com/hashicorp/go-msgpack v0.5.4/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=\ngithub.com/hashicorp/go-multierror v0.0.0-20180717150148-3d5d8f294aa0/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=\ngithub.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=\ngithub.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=\ngithub.com/hashicorp/go-plugin v1.0.1-0.20190610192547-a1bc61569a26/go.mod h1:++UyYGoz3o5w9ZzAdZxtQKrWWP+iqPBn3cQptSMzBuY=\ngithub.com/hashicorp/go-retryablehttp v0.5.2/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=\ngithub.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=\ngithub.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=\ngithub.com/hashicorp/go-slug v0.4.1/go.mod h1:I5tq5Lv0E2xcNXNkmx7BSfzi1PsJ2cNjs3cC3LwyhK8=\ngithub.com/hashicorp/go-sockaddr v0.0.0-20180320115054-6d291a969b86/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=\ngithub.com/hashicorp/go-tfe v0.3.27/go.mod h1:DVPSW2ogH+M9W1/i50ASgMht8cHP7NxxK0nrY9aFikQ=\ngithub.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE=\ngithub.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=\ngithub.com/hashicorp/go-version v1.1.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=\ngithub.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=\ngithub.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=\ngithub.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=\ngithub.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=\ngithub.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90=\ngithub.com/hashicorp/hcl/v2 v2.2.0 h1:ZQ1eNLggMfTyFBhV8swxT081mlaRjr4EG85NEjjLB84=\ngithub.com/hashicorp/hcl/v2 v2.2.0/go.mod h1:MD4q2LOluJ5pRwTVkCXmJOY7ODWDXVXGVB8LY0t7wig=\ngithub.com/hashicorp/hcl2 v0.0.0-20190821123243-0c888d1241f6/go.mod h1:Cxv+IJLuBiEhQ7pBYGEuORa0nr4U994pE8mYLuFd7v0=\ngithub.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590 h1:2yzhWGdgQUWZUCNK+AoO35V+HTsgEmcM4J9IkArh7PI=\ngithub.com/hashicorp/hil v0.0.0-20190212112733-ab17b08d6590/go.mod h1:n2TSygSNwsLJ76m8qFXTSc7beTb+auJxYdqrnoqwZWE=\ngithub.com/hashicorp/memberlist v0.1.0/go.mod h1:ncdBp14cuox2iFOq3kDiquKU6fqsTBc3W6JvZwjxxsE=\ngithub.com/hashicorp/serf v0.0.0-20160124182025-e4ec8cc423bb/go.mod h1:h/Ru6tmZazX7WO/GDmwdpS975F019L4t5ng5IgwbNrE=\ngithub.com/hashicorp/terraform v0.12.18 h1:U9gd/12wfT0Q7JYM43Hob6rcirICKCnxSDY+sJlYh6A=\ngithub.com/hashicorp/terraform v0.12.18/go.mod h1:wA1HxKwR2a21mNFaKyv1lQ+dAwtQKCKFfUAuTqPeP2U=\ngithub.com/hashicorp/terraform-config-inspect v0.0.0-20190821133035-82a99dc22ef4/go.mod h1:JDmizlhaP5P0rYTTZB0reDMefAiJyfWPEtugV4in1oI=\ngithub.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596 h1:hjyO2JsNZUKT1ym+FAdlBEkGPevazYsmVgIMw7dVELg=\ngithub.com/hashicorp/terraform-svchost v0.0.0-20191011084731-65d371908596/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=\ngithub.com/hashicorp/vault v0.10.4/go.mod h1:KfSyffbKxoVyspOdlaGVjIuwLobi07qD1bAbosPMpP0=\ngithub.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=\ngithub.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=\ngithub.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=\ngithub.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=\ngithub.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=\ngithub.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8 h1:12VvqtR6Aowv3l/EQUlocDHW2Cp4G9WJVH7uyH8QFJE=\ngithub.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=\ngithub.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=\ngithub.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=\ngithub.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=\ngithub.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=\ngithub.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926/go.mod h1:U+RSyWxWd04xTqnuOQxnai7XGS2PrPY2cfGoDKtMHjA=\ngithub.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=\ngithub.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=\ngithub.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=\ngithub.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=\ngithub.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=\ngithub.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU=\ngithub.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=\ngithub.com/karrick/godirwalk v1.15.3 h1:0a2pXOgtB16CqIqXTiT7+K9L73f74n/aNQUnH6Ortew=\ngithub.com/karrick/godirwalk v1.15.3/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=\ngithub.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=\ngithub.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=\ngithub.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=\ngithub.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=\ngithub.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=\ngithub.com/lusis/go-artifactory v0.0.0-20160115162124-7e4ce345df82/go.mod h1:y54tfGmO3NKssKveTEFFzH8C/akrSOy/iW9qEAUDV84=\ngithub.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=\ngithub.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI=\ngithub.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc=\ngithub.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY=\ngithub.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI=\ngithub.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI=\ngithub.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=\ngithub.com/masterzen/simplexml v0.0.0-20160608183007-4572e39b1ab9/go.mod h1:kCEbxUJlNDEBNbdQMkPSp6yaKcRXVI6f4ddk8Riv4bc=\ngithub.com/masterzen/winrm v0.0.0-20190223112901-5e5c9a7fe54b/go.mod h1:wr1VqkwW0AB5JS0QLy5GpVMS9E3VtRoSYXUYyVk46KY=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=\ngithub.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=\ngithub.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/mattn/go-shellwords v1.0.4/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=\ngithub.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=\ngithub.com/miekg/dns v1.0.8/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=\ngithub.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=\ngithub.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=\ngithub.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=\ngithub.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/go-linereader v0.0.0-20190213213312-1b945b3263eb/go.mod h1:OaY7UOoTkkrX3wRwjpYRKafIkkyeD0UtweSHAWWiqQM=\ngithub.com/mitchellh/go-testing-interface v0.0.0-20171004221916-a61a99592b77/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=\ngithub.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=\ngithub.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/go-wordwrap v1.0.0 h1:6GlHJ/LTGMrIJbwgdqdl2eEH8o+Exx/0m8ir9Gns0u4=\ngithub.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=\ngithub.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=\ngithub.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=\ngithub.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=\ngithub.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=\ngithub.com/mitchellh/prefixedio v0.0.0-20190213213902-5733675afd51/go.mod h1:kB1naBgV9ORnkiTVeyJOI1DavaJkG4oNIq0Af6ZVKUo=\ngithub.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=\ngithub.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=\ngithub.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=\ngithub.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=\ngithub.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=\ngithub.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=\ngithub.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=\ngithub.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=\ngithub.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=\ngithub.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=\ngithub.com/packer-community/winrmcp v0.0.0-20180102160824-81144009af58/go.mod h1:f6Izs6JvFTdnRbziASagjZ2vmf55NSIkC/weStxCHqk=\ngithub.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=\ngithub.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=\ngithub.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=\ngithub.com/posener/complete v1.2.1/go.mod h1:6gapUrK/U1TAN7ciCoNRIdVC5sbdBTUh1DKN0g6uH7E=\ngithub.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=\ngithub.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=\ngithub.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=\ngithub.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=\ngithub.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=\ngithub.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=\ngithub.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=\ngithub.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=\ngithub.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=\ngithub.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=\ngithub.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=\ngithub.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=\ngithub.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=\ngithub.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.4.0 h1:LUa41nrWTQNGhzdsZ5lTnkwbNjj6rXTdazA1cSdjkOY=\ngithub.com/rogpeppe/go-internal v1.4.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w=\ngithub.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=\ngithub.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=\ngithub.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=\ngithub.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=\ngithub.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=\ngithub.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=\ngithub.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=\ngithub.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=\ngithub.com/smartystreets/goconvey v0.0.0-20180222194500-ef6db91d284a/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=\ngithub.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=\ngithub.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=\ngithub.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=\ngithub.com/spf13/afero v1.2.1/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=\ngithub.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=\ngithub.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=\ngithub.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=\ngithub.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs=\ngithub.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=\ngithub.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=\ngithub.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=\ngithub.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=\ngithub.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=\ngithub.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=\ngithub.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=\ngithub.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=\ngithub.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=\ngithub.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=\ngithub.com/svanharmelen/jsonapi v0.0.0-20180618144545-0c0828c3f16d/go.mod h1:BSTlc8jOjh0niykqEGVXOLXdi9o0r0kR8tCYiMvjFgw=\ngithub.com/terraform-providers/terraform-provider-openstack v1.15.0/go.mod h1:2aQ6n/BtChAl1y2S60vebhyJyZXBsuAI5G4+lHrT1Ew=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20171017195756-830351dc03c6/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=\ngithub.com/ugorji/go v0.0.0-20180813092308-00b869d2f4a5/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=\ngithub.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=\ngithub.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=\ngithub.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=\ngithub.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/vmihailenco/msgpack v4.0.1+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=\ngithub.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=\ngithub.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=\ngithub.com/xlab/treeprint v0.0.0-20161029104018-1d6e34225557/go.mod h1:ce1O1j6UtZfjr22oyGxGLbauSBp2YVXpARAosm7dHBg=\ngithub.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=\ngithub.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/zclconf/go-cty v1.0.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty v1.1.1 h1:Shl2p9Dat0cqJfXu0DZa+cOTRPhXQjK8IYWD6GVfiqo=\ngithub.com/zclconf/go-cty v1.1.1/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=\ngithub.com/zclconf/go-cty-yaml v1.0.1 h1:up11wlgAaDvlAGENcFDnZgkn0qUJurso7k6EpURKNF8=\ngithub.com/zclconf/go-cty-yaml v1.0.1/go.mod h1:IP3Ylp0wQpYm50IHK8OZWKMu6sPJIUgKa8XhiVHura0=\ngo.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=\ngo.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=\ngo.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=\ngo.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=\ngo.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=\ngo.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngo.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=\ngolang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo=\ngolang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A=\ngolang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=\ngolang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg=\ngolang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=\ngolang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=\ngolang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=\ngolang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=\ngolang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=\ngolang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=\ngolang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f h1:J5lckAjkw6qYlOZNj90mLYNTEKDvWeuc1yieZ8qUzUE=\ngolang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367 h1:0IiAsCRByjO2QjX7ZPkw5oU9x+n1YqRL802rjC0c3Aw=\ngolang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=\ngolang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20191009170851-d66e71096ffb h1:TR699M2v0qoKTOHxeLgp6zPqaQNs74f01a/ob9W0qko=\ngolang.org/x/net v0.0.0-20191009170851-d66e71096ffb/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=\ngolang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=\ngolang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190502175342-a43fa875dd82 h1:vsphBvatvfbhlb4PO1BYSr9dzugGxJ/SQHoNufZJq1w=\ngolang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190515120540-06a5c4944438 h1:khxRGsvPk4n2y8I/mLLjp7e5dMTJmH75wvqS6nMwUtY=\ngolang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867 h1:JoRuNIf+rpHl+VhScRQQvzbHed86tKkqwPMV34T8myw=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=\ngolang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=\ngolang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=\ngolang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=\ngolang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=\ngolang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0 h1:Dh6fw+p6FyRl5x/FvNswO1ji0lIGzm3KP8Y9VkS9PTE=\ngolang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=\ngolang.org/x/tools v0.0.0-20191004055002-72853e10c5a3 h1:2AmBLzhAfXj+2HCW09VCkJtHIYgHTIPcTeYqgP7Bwt0=\ngolang.org/x/tools v0.0.0-20191004055002-72853e10c5a3/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114 h1:DnSr2mCsxyCE6ZgIkmcWUQY2R5cH/6wL7eIxEmQOMSE=\ngolang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200106190116-7be0a674c9fc h1:MR2F33ipDGog0C4eMhU6u9o3q6c3dvYis2aG6Jl12Wg=\ngolang.org/x/tools v0.0.0-20200106190116-7be0a674c9fc/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200107184032-11e9d9cc0042 h1:BKiPVwWbEdmAh+5CBwk13CYeVJQRDJpDnKgDyMOGz9M=\ngolang.org/x/tools v0.0.0-20200107184032-11e9d9cc0042/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200108195415-316d2f248479 h1:csuS+MHeEA2eWhyjQCMaPMq4z1+/PohkBSjJZHSIbOE=\ngolang.org/x/tools v0.0.0-20200108195415-316d2f248479/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117065230-39095c1d176c h1:FodBYPZKH5tAN2O60HlglMwXGAeV/4k+NKbli79M/2c=\ngolang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c h1:2EA2K0k9bcvvEDlqD8xdlOhCOqq+O/p9Voqi4x9W1YU=\ngolang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200117170720-ade7f2547e48 h1:XfvHzzsGwwI2bSS5CSATpEdarP7UY+5bU6A0/50E5bE=\ngolang.org/x/tools v0.0.0-20200117170720-ade7f2547e48/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2 h1:0sfSpGSa544Fwnbot3Oxq/U6SXqjty6Jy/3wRhVS7ig=\ngolang.org/x/tools v0.0.0-20200216192241-b320d3a0f5a2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200218190056-dc8ced655da2 h1:QkSrkYCPqctY2iLj509pqdbwIc+uqqiuvr0PzJE+4j0=\ngolang.org/x/tools v0.0.0-20200218190056-dc8ced655da2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200218205902-f8e42dc47720 h1:90L2fHeLQmQFe04F648NKZE5sQP/M/6CjHcjtM7jP5U=\ngolang.org/x/tools v0.0.0-20200218205902-f8e42dc47720/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d h1:ZQ18He7VORO2x4IEBuwfdp56K+ftEzRjvL0cFuCGCcM=\ngolang.org/x/tools v0.0.0-20200219195521-7c4b6277d74d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200219203042-77adbdfd2c36 h1:BCZjrbHHQTu+xrwLsm/PiTuyEPgFnl82o2AGXB36sH4=\ngolang.org/x/tools v0.0.0-20200219203042-77adbdfd2c36/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200219211314-d3d48f09aa81 h1:rHjptmxnKy58RQ6SLKtBl0aRmpTSEMJqbM4GRMlYeUA=\ngolang.org/x/tools v0.0.0-20200219211314-d3d48f09aa81/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200220051852-2086a0a691c0 h1:K3udS2sjmoy0P+/d56DdLRrwHKTCkW+A179fa3swkyQ=\ngolang.org/x/tools v0.0.0-20200220051852-2086a0a691c0/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200220224806-8a925fa4c0df h1:oQFmpjL+rb2xLkgPCpdGSAGDdq1geTZVvIfCMxSB/wQ=\ngolang.org/x/tools v0.0.0-20200220224806-8a925fa4c0df/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200221152158-fe62aff31966 h1:anduI32gSABIQHvwp/y8hEslPaXuVTh5+qw7Eb4Js9c=\ngolang.org/x/tools v0.0.0-20200221152158-fe62aff31966/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200221191710-57f3fb51f507 h1:zE128a8BUJqwFqwi8LxUnOdV3eSOGIzDhiIV/QW8eXc=\ngolang.org/x/tools v0.0.0-20200221191710-57f3fb51f507/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200221224223-e1da425f72fd h1:hHkvGJK23seRCflePJnVa9IMv8fsuavSCWKd11kDQFs=\ngolang.org/x/tools v0.0.0-20200221224223-e1da425f72fd/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2 h1:L/G4KZvrQn7FWLN/LlulBtBzrLUhqjiGfTWWDmrh+IQ=\ngolang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200225022059-a0ec867d517c h1:cmkqWf0jTLsPn3dn28dkzCF+MoDvuZS7pTwHwGmkqiU=\ngolang.org/x/tools v0.0.0-20200225022059-a0ec867d517c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200227193342-b3f10971cb29 h1:gZO+3X1BaUgKEk78zRnb5VySro9NQVkdA1UakZx/Ojg=\ngolang.org/x/tools v0.0.0-20200227193342-b3f10971cb29/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200228135638-5c7c66ced534 h1:XVzrScQUlfS6ssloilmEJdJhlMDtnculCx+0zmVHSA8=\ngolang.org/x/tools v0.0.0-20200228135638-5c7c66ced534/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200228194328-a628ca32ebc0 h1:bC/KxYpsTCppyXiguiVppCnZLkQpMNIi042/S5bYanA=\ngolang.org/x/tools v0.0.0-20200228194328-a628ca32ebc0/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200304143113-d6a4d55695f2 h1:fRkP4IAibCxA/Xm3eihEsPrwWG5MniPSM5zrl9GfOrM=\ngolang.org/x/tools v0.0.0-20200304143113-d6a4d55695f2/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb h1:iKlO7ROJc6SttHKlxzwGytRtBUqX4VARrNTgP2YLX5M=\ngolang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200305140159-d7d444866696 h1:uuiLBSsR+ZDddgZ/2k23Y7FrUNl29gq4sEFcO170R5k=\ngolang.org/x/tools v0.0.0-20200305140159-d7d444866696/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200305185322-6a641547f55b h1:kWwtroURwYKTlrhKtLws/aJ3iuNdVB417e2FCSutkIs=\ngolang.org/x/tools v0.0.0-20200305185322-6a641547f55b/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200306135127-136c3617b60f h1:kVHafb5NzUbarQdE1pOf582kIWNUjHPsfJ1bebp0T/U=\ngolang.org/x/tools v0.0.0-20200306135127-136c3617b60f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200306191617-51e69f71924f h1:bFIWQKTZ5vXyr7xMDvzbWUj5Y/WBE4a4sf35MAyZjx0=\ngolang.org/x/tools v0.0.0-20200306191617-51e69f71924f/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE=\ngolang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200312194400-c312e98713c2 h1:6TB4+MaZlkcSsJDu+BS5yxSEuZIYhjWz+jhbSLEZylI=\ngolang.org/x/tools v0.0.0-20200312194400-c312e98713c2/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=\ngolang.org/x/tools v0.0.0-20200313205530-4303120df7d8 h1:gkI/wGGwpcG5W4hLCzZNGxA4wzWBGGDStRI1MrjDl2Q=\ngolang.org/x/tools v0.0.0-20200313205530-4303120df7d8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200316182129-bd88ce97550a h1:Q6kCYeKXRz1J3D2x7fnZ16MoYsHpTgpaOR/8Vw9L7aM=\ngolang.org/x/tools v0.0.0-20200316182129-bd88ce97550a/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200317043434-63da46f3035e h1:8ogAbHWoJTPepnVbNRqXLOpzMkl0rtRsM7crbflc4XM=\ngolang.org/x/tools v0.0.0-20200317043434-63da46f3035e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200317184713-827390e9012e h1:LuTra/PQ8QHieyP2k0M0dZbF1e6fDxO7k3PqSPDxNc4=\ngolang.org/x/tools v0.0.0-20200317184713-827390e9012e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200317190434-d05f4d6edb1c h1:RO17pvmq2QtgOGu4jHRnfyUW3VB9v6zlpzL1HqBfYCI=\ngolang.org/x/tools v0.0.0-20200317190434-d05f4d6edb1c/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200317191843-cf1dd6fc347b h1:lDcwTl/by9TEfMDCbwUiqkMZbFJqs0RpmOzPrqBEN/U=\ngolang.org/x/tools v0.0.0-20200317191843-cf1dd6fc347b/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200317205521-2944c61d58b4 h1:ZDNUPyBE3vtF6Sm7/3g98znzRSeFeCuIuAFxYQLm21o=\ngolang.org/x/tools v0.0.0-20200317205521-2944c61d58b4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200318150045-ba25ddc85566 h1:OXjomkWHhzUx4+HldlJ2TsMxJdWgEo5CTtspD1wdhdk=\ngolang.org/x/tools v0.0.0-20200318150045-ba25ddc85566/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200319210407-521f4a0cd458 h1:DgonIcqC7u+gVZX7lpuReBil5B/i8fvW/hAQdhT6/ao=\ngolang.org/x/tools v0.0.0-20200319210407-521f4a0cd458/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200320205904-2f9d11aa233c h1:F67BC4jYRCvm2oIVEOY3X9eXKtuMWqsBMByOGlzBgBo=\ngolang.org/x/tools v0.0.0-20200320205904-2f9d11aa233c/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e h1:ssd5ulOvVWlh4kDSUF2SqzmMeWfjmwDXM+uGw/aQjRE=\ngolang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200323171731-e609210bcd3f h1:dlzvbS5gAya85bCBRskGDmUQ27VayugwRFHdr4dy3dU=\ngolang.org/x/tools v0.0.0-20200323171731-e609210bcd3f/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200323192200-8849913b6971 h1:7xTjEBkQgB1qdLZddETld2Flxf5D/U848jdOyhu39TE=\ngolang.org/x/tools v0.0.0-20200323192200-8849913b6971/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200323210725-ef1313dc6d0a h1:QpsrlnR31DN2tCItRzamUXN4oAe3U00ru8f0ik9HSYI=\ngolang.org/x/tools v0.0.0-20200323210725-ef1313dc6d0a/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200324182314-a5e5fedfe742 h1:C4Jhhgnr4nUar2rZdLD0pSC6YvxiZgmp4Fl904gxSXU=\ngolang.org/x/tools v0.0.0-20200324182314-a5e5fedfe742/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200324201824-1fc30e1f4ccc h1:pdV61EOqqmHmRMCtqTv+eoY+K04cmKfr4R8aA9ysfgY=\ngolang.org/x/tools v0.0.0-20200324201824-1fc30e1f4ccc/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=\ngolang.org/x/tools v0.0.0-20200406213809-066fd1390ee0 h1:PaUgOASiqoF4KlotK7/3XKYFGN5Hw5jh/SHqOVpQBcI=\ngolang.org/x/tools v0.0.0-20200406213809-066fd1390ee0/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=\ngoogle.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=\ngoogle.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=\ngoogle.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=\ngoogle.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=\ngoogle.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=\ngoogle.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=\ngoogle.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=\ngoogle.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=\ngoogle.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=\ngoogle.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=\ngoogle.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=\ngoogle.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngoogle.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=\ngopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=\ngopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=\ngopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=\ngopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=\ngopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=\ngopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=\ngopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\nhonnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhonnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=\nhowett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=\nrsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=\n"
  },
  {
    "path": "linter/common.go",
    "content": "package linter\n\nimport (\n\t\"encoding/csv\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\nfunc readContent(filename string) ([]byte, error) {\n\tif filename == \"-\" {\n\t\treturn ioutil.ReadAll(os.Stdin)\n\t}\n\treturn ioutil.ReadFile(filename)\n}\n\nfunc loadYAML(filename string) ([]interface{}, error) {\n\tempty := []interface{}{}\n\tcontent, err := readContent(filename)\n\tif err != nil {\n\t\treturn empty, err\n\t}\n\tvar result []interface{}\n\tfor _, doc := range splitYAMLDocs(string(content)) {\n\t\tvar yamlData interface{}\n\t\terr = yaml.Unmarshal([]byte(doc), &yamlData)\n\t\tif err != nil {\n\t\t\treturn empty, err\n\t\t}\n\t\tswitch v := yamlData.(type) {\n\t\tcase nil:\n\t\t\t// ignore empty document - probably only contains comments\n\t\tcase map[string]interface{}:\n\t\t\t// Non-empty document should contain an object (not a list for example)\n\t\t\tresult = append(result, v)\n\t\tdefault:\n\t\t\treturn []interface{}{}, errors.New(\"YAML in unexpected format\")\n\t\t}\n\t}\n\treturn result, nil\n}\n\nfunc splitYAMLDocs(content string) []string {\n\tdocs := []string{}\n\tdoc := \"\"\n\tlines := strings.Split(content, \"\\n\")\n\tfor _, line := range lines {\n\t\tif line == \"---\" {\n\t\t\tif docIsNotEmpty(doc) {\n\t\t\t\tdocs = append(docs, doc)\n\t\t\t}\n\t\t\tdoc = \"\"\n\t\t} else {\n\t\t\tdoc = doc + line + \"\\n\"\n\t\t}\n\t}\n\tif docIsNotEmpty(doc) {\n\t\tdocs = append(docs, doc)\n\t}\n\treturn docs\n}\n\nfunc docIsNotEmpty(s string) bool {\n\treturn strings.TrimSpace(s) != \"\"\n}\n\nfunc loadJSON(filename string) ([]interface{}, error) {\n\tempty := []interface{}{}\n\tcontent, err := readContent(filename)\n\tif err != nil {\n\t\treturn empty, err\n\t}\n\n\tvar jsonData interface{}\n\terr = json.Unmarshal(content, &jsonData)\n\tif err != nil {\n\t\treturn empty, err\n\t}\n\tm := jsonData.(map[string]interface{})\n\treturn []interface{}{m}, nil\n}\n\nfunc loadCSV(filename string) ([][]string, error) {\n\tcontent, err := readContent(filename)\n\tif err != nil {\n\t\treturn [][]string{}, err\n\t}\n\treturn csv.NewReader(strings.NewReader(string(content))).ReadAll()\n}\n\nfunc getResourceIDFromFilename(filename string) string {\n\t_, resourceID := filepath.Split(filename)\n\treturn resourceID\n}\n\n// CombineValidationReports merges results from two separate Validate runs\nfunc CombineValidationReports(r1, r2 assertion.ValidationReport) assertion.ValidationReport {\n\treturn assertion.ValidationReport{\n\t\tFilesScanned:     append(r1.FilesScanned, r2.FilesScanned...),\n\t\tResourcesScanned: append(r1.ResourcesScanned, r2.ResourcesScanned...),\n\t\tViolations:       append(r1.Violations, r2.Violations...),\n\t}\n}\n"
  },
  {
    "path": "linter/common_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestLoadYamlFileError(t *testing.T) {\n\t_, err := loadYAML(\"does-not-exist.yml\")\n\tif err == nil {\n\t\tt.Errorf(\"LoadYaml should fail for missing file\")\n\t}\n}\n\nfunc TestLoadYamlParseError(t *testing.T) {\n\t_, err := loadYAML(\"./testdata/resources/invalid.yml\")\n\tif err == nil {\n\t\tt.Errorf(\"LoadYaml should fail for file with invalid YAML\")\n\t}\n\tif !strings.Contains(err.Error(), \"error converting YAML to JSON\") {\n\t\tt.Errorf(\"Expecting parse error for invalid YAML\")\n\t}\n}\n\nfunc TestLoadYamlUnexpectedFormat(t *testing.T) {\n\t_, err := loadYAML(\"./testdata/rules/bad-format.yml\")\n\tassert.NotNil(t, err, \"YAML with unexpected format should return error\")\n\tassert.Contains(t, err.Error(), \"YAML in unexpected format\")\n}\n\nfunc TestLoadYamlMultipleDocuments(t *testing.T) {\n\tdocs, err := loadYAML(\"./testdata/resources/multiple_pods.yml\")\n\tassert.Nil(t, err, \"Expecting Load to not return an error\")\n\tassert.Equal(t, 2, len(docs), \"Expecting loader to find 2 documents\")\n}\n\nfunc TestLoadYamlWithEmbeddedYaml(t *testing.T) {\n\tdocs, err := loadYAML(\"./testdata/resources/embedded_yaml.yml\")\n\tassert.Nil(t, err, \"Expecting Load to not return an error\")\n\tassert.Equal(t, 2, len(docs), \"Expecting loader to find 2 documents\")\n}\n\nfunc TestLoadYamlWithEmptyDocument(t *testing.T) {\n\tdocs, err := loadYAML(\"./testdata/resources/empty_document.yml\")\n\tassert.Nil(t, err, \"Expecting Load to not return an error\")\n\tassert.Equal(t, 1, len(docs), \"Expecting loader to find 1 document\")\n}\n\nfunc TestGetResourceIDFromFilename(t *testing.T) {\n\texpected := \"resource.yml\"\n\tn := getResourceIDFromFilename(\"path/to/resource.yml\")\n\tif n != expected {\n\t\tt.Errorf(\"expecting getResourceIDFromFilename returned %s, expected '%s'\", n, expected)\n\t}\n}\n\nfunc TestCombineValidationReports(t *testing.T) {\n\tr1 := assertion.ValidationReport{FilesScanned: []string{\"one\"}}\n\tr2 := assertion.ValidationReport{FilesScanned: []string{\"two\"}}\n\tr := CombineValidationReports(r1, r2)\n\tif len(r.FilesScanned) != 2 {\n\t\tt.Errorf(\"expecting CombineValidationReports to have 2 entries for FilesScanned\")\n\t}\n}\n"
  },
  {
    "path": "linter/csv_resource_loader.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"path/filepath\"\n)\n\n// CSVResourceLoader loads a list of Resource objects based on the list of ResourceConfig objects\ntype CSVResourceLoader struct {\n\tColumns []assertion.ColumnConfig\n}\n\nfunc extractCSVResourceID(expression string, properties interface{}) string {\n\tresourceID := \"None\"\n\tresult, err := assertion.SearchData(expression, properties)\n\tif err == nil {\n\t\tresourceID, _ = result.(string)\n\t}\n\treturn resourceID\n}\n\n// Load converts a text file into a collection of Resource objects\nfunc (l CSVResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: make([]assertion.Resource, 0),\n\t}\n\tcsvRows, err := loadCSV(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tfor rowNumber, row := range csvRows {\n\t\tproperties := map[string]interface{}{}\n\t\tproperties[\"__file__\"] = filename\n\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\tfor columnNumber, columnConfig := range l.Columns {\n\t\t\tproperties[columnConfig.Name] = row[columnNumber]\n\t\t}\n\t\tresource := assertion.Resource{\n\t\t\tID:         string(rowNumber),\n\t\t\tType:       \"row\",\n\t\t\tProperties: properties,\n\t\t\tFilename:   filename,\n\t\t}\n\t\tloaded.Resources = append(loaded.Resources, resource)\n\t}\n\treturn loaded, nil\n}\n\n// PostLoad does no additional processing fro a CSVResourceLoader\nfunc (l CSVResourceLoader) PostLoad(r FileResources) ([]assertion.Resource, error) {\n\treturn r.Resources, nil\n}\n"
  },
  {
    "path": "linter/csv_resource_loader_test.go",
    "content": "package linter\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestCSVLinterValidate(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-csv.yml\", t)\n\tfilenames := []string{\"./testdata/resources/users.csv\"}\n\tloader := CSVResourceLoader{Columns: ruleSet.Columns}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, 3, len(report.ResourcesScanned), \"Expecting Validate to scan 3 resources\")\n\tassert.Equal(t, 1, len(report.Violations), \"Expecting Validate to find 1 violation\")\n}\n\nfunc TestCSVLinterSearch(t *testing.T) {\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-csv.yml\", t)\n\tfilenames := []string{\"./testdata/resources/users.csv\"}\n\tloader := CSVResourceLoader{Columns: ruleSet.Columns}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\tvar b bytes.Buffer\n\tlinter.Search(ruleSet, \"Department\", &b)\n\tassert.Contains(t, b.String(), \"Audit\", \"Expecting TestCSVLinterSearch to find string in output\")\n}\n"
  },
  {
    "path": "linter/file_linter.go",
    "content": "package linter\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\ntype (\n\t// Variable contains a key value pair for expressions in a Terraform configuration file\n\tVariable struct {\n\t\tName  string\n\t\tValue interface{}\n\t}\n\t// FileResources contains the variables and resources loaded from a collection of files\n\tFileResources struct {\n\t\tResources []assertion.Resource\n\t\tVariables []Variable\n\t}\n\t// FileResourceLoader provides the interface that a Linter needs to load a collection of Resource objects\n\tFileResourceLoader interface {\n\t\tLoad(filename string) (FileResources, error)\n\t\tPostLoad(resources FileResources) ([]assertion.Resource, error)\n\t}\n\t// FileLinter provides implementation for some common functions that are used by multiple Linter implementations\n\tFileLinter struct {\n\t\tFilenames   []string\n\t\tValueSource assertion.ValueSource\n\t\tLoader      FileResourceLoader\n\t}\n)\n\n// Validate validates a collection of filenames using a RuleSet\nfunc (fl FileLinter) Validate(ruleSet assertion.RuleSet, options Options) (assertion.ValidationReport, error) {\n\trules := assertion.FilterRulesByTagAndID(ruleSet.Rules, options.Tags, options.RuleIDs, options.IgnoreRuleIDs)\n\trl := ResourceLinter{ValueSource: fl.ValueSource}\n\n\tresources := []assertion.Resource{}\n\tvariables := []Variable{}\n\tfilesScanned := []string{}\n\n\tloadViolations := []assertion.Violation{}\n\tvar resourcesToValidate []assertion.Resource\n\n\t//TODO: This is ugly in several ways\n\tif tf12Loader, ok := fl.Loader.(Terraform12ResourceLoader); ok {\n\t\tfilteredFilenames := filterFiles(fl.Filenames, ruleSet.Files)\n\t\tresult, err := tf12Loader.LoadMany(filteredFilenames)\n\t\tif err != nil {\n\t\t\t//TODO: It would probably be nice if we mapped this back to the correct file\n\t\t\tloadViolations = append(loadViolations, makeLoadViolation(fl.Filenames[0], err))\n\t\t}\n\t\tresourcesToValidate = result.Resources\n\t\tfilesScanned = append(filesScanned, fl.Filenames...)\n\t} else {\n\t\tfilesScanned, loadViolations, resources, variables = iterateFiles(fl, ruleSet, filesScanned, loadViolations, resources, variables)\n\t\tvar err error\n\t\tresourcesToValidate, err = fl.Loader.PostLoad(FileResources{Resources: resources, Variables: variables})\n\t\tif err != nil {\n\t\t\treturn assertion.ValidationReport{}, err\n\t\t}\n\t}\n\n\treport, err := rl.ValidateResources(resourcesToValidate, rules)\n\tif err != nil {\n\t\treturn report, err\n\t}\n\treport.FilesScanned = filesScanned\n\treport.Violations = append(report.Violations, loadViolations...)\n\treturn report, nil\n}\n\nfunc filterFiles(fileNames []string, patterns []string) (ret []string) {\n\tfor _, s := range fileNames {\n\t\tok, _ := assertion.ShouldIncludeFile(patterns, s)\n\t\tif ok {\n\t\t\tret = append(ret, s)\n\t\t}\n\t}\n\treturn\n}\n\nfunc iterateFiles(fl FileLinter, ruleSet assertion.RuleSet, filesScanned []string, loadViolations []assertion.Violation, resources []assertion.Resource, variables []Variable) ([]string, []assertion.Violation, []assertion.Resource, []Variable) {\n\tfor _, filename := range fl.Filenames {\n\t\tinclude, err := assertion.ShouldIncludeFile(ruleSet.Files, filename)\n\t\tif err == nil && include {\n\t\t\tassertion.Debugf(\"Processing %s\\n\", filename)\n\t\t\tfilesScanned = append(filesScanned, filename)\n\t\t\tloaded, err := fl.Loader.Load(filename)\n\t\t\tif err != nil {\n\t\t\t\tloadViolations = append(loadViolations, makeLoadViolation(filename, err))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tassertion.Debugf(\"Found variables %v\\n\", loaded.Variables)\n\t\t\tresources = append(resources, loaded.Resources...)\n\t\t\tvariables = append(variables, loaded.Variables...)\n\t\t}\n\t}\n\treturn filesScanned, loadViolations, resources, variables\n}\n\nfunc makeLoadViolation(filename string, err error) assertion.Violation {\n\treturn assertion.Violation{\n\t\tRuleID:           \"FILE_LOAD\",\n\t\tResourceID:       filename,\n\t\tResourceType:     \"file\",\n\t\tCategory:         \"load\",\n\t\tStatus:           \"FAILURE\",\n\t\tRuleMessage:      \"Unable to load file\",\n\t\tAssertionMessage: err.Error(),\n\t\tFilename:         filename,\n\t\tCreatedAt:        time.Now().UTC().Format(time.RFC3339),\n\t}\n}\n\n// Search evaluates a JMESPath expression against resources in a collection of filenames\nfunc (fl FileLinter) Search(ruleSet assertion.RuleSet, searchExpression string, w io.Writer) {\n\tresources := []assertion.Resource{}\n\tvariables := []Variable{}\n\tfor _, filename := range fl.Filenames {\n\t\tinclude, _ := assertion.ShouldIncludeFile(ruleSet.Files, filename) // FIXME what about error?\n\t\tif include {\n\t\t\tfmt.Fprintf(w, \"Searching %s:\\n\", filename)\n\t\t\tloaded, err := fl.Loader.Load(filename)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(w, \"Error for file:\", filename)\n\t\t\t\tfmt.Fprintln(w, err.Error())\n\t\t\t}\n\t\t\tresources = append(resources, loaded.Resources...)\n\t\t\tvariables = append(variables, loaded.Variables...)\n\t\t}\n\t}\n\tresourcesToSearch, err := fl.Loader.PostLoad(FileResources{Resources: resources, Variables: variables})\n\tif err != nil {\n\t\tfmt.Fprintln(w, err.Error())\n\t\treturn\n\t}\n\tfor _, resource := range resourcesToSearch {\n\t\tv, err := assertion.SearchData(searchExpression, resource.Properties)\n\t\tif err != nil {\n\t\t\tfmt.Fprintln(w, err)\n\t\t} else {\n\t\t\ts, err := assertion.JSONStringify(v)\n\t\t\tif err != nil {\n\t\t\t\tfmt.Fprintln(w, err)\n\t\t\t} else {\n\t\t\t\tfmt.Fprintf(w, \"%s (%s): %s\\n\", resource.ID, resource.Type, s)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "linter/file_linter_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestFilterFiles(t *testing.T) {\n\tinput := []string {\"bad.txt\", \"good.tf\"}\n\tfilters := [] string {\"*.tf\"}\n\n\tactual := filterFiles(input, filters)\n\n\tassert.Equal(t, 1, len(actual))\n}\n"
  },
  {
    "path": "linter/helpers_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"io/ioutil\"\n\t\"testing\"\n)\n\nfunc loadRulesForTest(filename string, t *testing.T) assertion.RuleSet {\n\trulesContent, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\tt.Errorf(\"Unable to load rules file: %s\", filename)\n\t\treturn assertion.RuleSet{}\n\t}\n\truleSet, err := assertion.ParseRules(string(rulesContent))\n\tif err != nil {\n\t\tt.Errorf(\"Unable to parse rules file: %s\", filename)\n\t\treturn assertion.RuleSet{}\n\t}\n\treturn ruleSet\n}\n\nfunc assertViolationsCount(testName string, count int, violations []assertion.Violation, t *testing.T) {\n\tif len(violations) != count {\n\t\tt.Errorf(\"TestTerraformDataObject returned %d violations, expecting %d\", len(violations), count)\n\t\tt.Errorf(\"Violations: %v\", violations)\n\t}\n}\n\nfunc assertViolationByRuleID(testName string, ruleID string, violations []assertion.Violation, t *testing.T) {\n\tfound := false\n\truleIDsFound := []string{}\n\tfor _, v := range violations {\n\t\truleIDsFound = append(ruleIDsFound, v.RuleID)\n\t\tif v.RuleID == ruleID {\n\t\t\tfound = true\n\t\t}\n\t}\n\tif !found {\n\t\tt.Errorf(\"%s expected violation %s not found in %v\", testName, ruleID, ruleIDsFound)\n\t}\n}\n"
  },
  {
    "path": "linter/json_resource_loader.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"path/filepath\"\n)\n\n// JSONResourceLoader loads a list of Resource objects based on the list of ResourceConfig objects\ntype JSONResourceLoader struct {\n\tResources []assertion.ResourceConfig\n}\n\nfunc extractJSONResourceID(expression string, properties interface{}) string {\n\tresourceID := \"None\"\n\tresult, err := assertion.SearchData(expression, properties)\n\tif err == nil {\n\t\tresourceID, _ = result.(string)\n\t}\n\treturn resourceID\n}\n\n// Load converts a text file into a collection of Resource objects\nfunc (l JSONResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: make([]assertion.Resource, 0),\n\t}\n\tjsonResources, err := loadJSON(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tfor _, document := range jsonResources {\n\t\tfor _, resourceConfig := range l.Resources {\n\t\t\tmatches, err := assertion.SearchData(resourceConfig.Key, document)\n\t\t\tif err != nil {\n\t\t\t\treturn loaded, nil\n\t\t\t}\n\t\t\tsliceOfProperties, ok := matches.([]interface{})\n\t\t\tif ok {\n\t\t\t\tfor _, element := range sliceOfProperties {\n\t\t\t\t\tproperties := element.(map[string]interface{})\n\t\t\t\t\tproperties[\"__file__\"] = filename\n\t\t\t\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\t\t\t\tresource := assertion.Resource{\n\t\t\t\t\t\tID:         extractJSONResourceID(resourceConfig.ID, properties),\n\t\t\t\t\t\tType:       resourceConfig.Type,\n\t\t\t\t\t\tProperties: properties,\n\t\t\t\t\t\tFilename:   filename,\n\t\t\t\t\t}\n\t\t\t\t\tloaded.Resources = append(loaded.Resources, resource)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn loaded, nil\n}\n\n// PostLoad does no additional processing fro a JSONResourceLoader\nfunc (l JSONResourceLoader) PostLoad(r FileResources) ([]assertion.Resource, error) {\n\treturn r.Resources, nil\n}\n"
  },
  {
    "path": "linter/json_resource_loader_test.go",
    "content": "package linter\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestJSONLinterValidate(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-json.yml\", t)\n\tfilenames := []string{\"./testdata/resources/users.json\"}\n\tloader := JSONResourceLoader{Resources: ruleSet.Resources}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, 3, len(report.ResourcesScanned), \"Expecting Validate to scan 3 resources\")\n\tassert.Equal(t, 1, len(report.FilesScanned), \"Expecting Validate to scan 1 file\")\n\tassert.Equal(t, 1, len(report.Violations), \"Expecting Validate to find 1 violation\")\n}\n\nfunc TestJSONLinterSearch(t *testing.T) {\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-json.yml\", t)\n\tfilenames := []string{\"./testdata/resources/users.json\"}\n\tloader := JSONResourceLoader{Resources: ruleSet.Resources}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\tvar b bytes.Buffer\n\tlinter.Search(ruleSet, \"Department\", &b)\n\tassert.Contains(t, b.String(), \"Audit\", \"Expecting TestJSONLinterSearch to find string in output\")\n}\n"
  },
  {
    "path": "linter/kubernetes.go",
    "content": "package linter\n\nimport (\n\t\"errors\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"path/filepath\"\n)\n\n// KubernetesLinter lints resources in Kubernets YAML files\ntype KubernetesLinter struct {\n\tFilenames   []string\n\tValueSource assertion.ValueSource\n}\n\n// KubernetesResourceLoader converts Kubernetes configuration files into a collection of Resource objects\ntype KubernetesResourceLoader struct{}\n\nfunc getResourceIDFromMetadata(m map[string]interface{}) (string, bool) {\n\tif metadata, ok := m[\"metadata\"].(map[string]interface{}); ok {\n\t\tif name, ok := metadata[\"name\"].(string); ok {\n\t\t\treturn name, true\n\t\t}\n\t}\n\treturn \"\", false\n}\n\n// Load converts a text file into a collection of Resource objects\nfunc (l KubernetesResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: make([]assertion.Resource, 0),\n\t}\n\tyamlResources, err := loadYAML(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tfor _, resource := range yamlResources {\n\t\tproperties := resource.(map[string]interface{})\n\t\tvar resourceID string\n\t\tif name, ok := getResourceIDFromMetadata(properties); ok {\n\t\t\tresourceID = name\n\t\t} else {\n\t\t\tresourceID = getResourceIDFromFilename(filename)\n\t\t}\n\t\tproperties[\"__file__\"] = filename\n\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\tkind, ok := properties[\"kind\"].(string)\n\t\tif ok {\n\t\t\tkr := assertion.Resource{\n\t\t\t\tID:         resourceID,\n\t\t\t\tType:       kind,\n\t\t\t\tProperties: properties,\n\t\t\t\tFilename:   filename,\n\t\t\t}\n\t\t\tloaded.Resources = append(loaded.Resources, kr)\n\t\t} else {\n\t\t\treturn loaded, errors.New(\"Missing kind attribute\")\n\t\t}\n\t}\n\treturn loaded, nil\n}\n\n// PostLoad does no additional processing for a KubernetesLoader\nfunc (l KubernetesResourceLoader) PostLoad(r FileResources) ([]assertion.Resource, error) {\n\treturn r.Resources, nil\n}\n"
  },
  {
    "path": "linter/kubernetes_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"path/filepath\"\n\t\"testing\"\n)\n\nfunc TestKubernetesSpecialVariables(t *testing.T) {\n\tloader := KubernetesResourceLoader{}\n\tfilename := \"./testdata/resources/pod.yml\"\n\tloaded, err := loader.Load(filename)\n\tassert.Nil(t, err, \"Expecting Load to not return an error\")\n\tfor _, resource := range loaded.Resources {\n\t\tproperties := resource.Properties.(map[string]interface{})\n\t\tassert.Equal(t, filename, properties[\"__file__\"])\n\t\tassert.Equal(t, filepath.Dir(filename), properties[\"__dir__\"])\n\t}\n}\n\nfunc TestKubernetesMissingKind(t *testing.T) {\n\tloader := KubernetesResourceLoader{}\n\tfilename := \"./testdata/resources/missing_kind.yml\"\n\t_, err := loader.Load(filename)\n\tassert.NotNil(t, err, \"Expecting Load to not return an error\")\n\tassert.Contains(t, err.Error(), \"Missing kind attribute\")\n}\n"
  },
  {
    "path": "linter/linter.go",
    "content": "package linter\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\ntype (\n\t// Linter provides the interface for all supported linters\n\tLinter interface {\n\t\tValidate(ruleSet assertion.RuleSet, options Options) (assertion.ValidationReport, error)\n\t\tSearch(ruleSet assertion.RuleSet, searchExpression string, w io.Writer)\n\t}\n\n\t// Options configures what resources will be linted\n\tOptions struct {\n\t\tTags          []string\n\t\tRuleIDs       []string\n\t\tIgnoreRuleIDs []string\n\t}\n)\n\n// NewLinter create the right kind of Linter based on the type argument\nfunc NewLinter(ruleSet assertion.RuleSet, vs assertion.ValueSource, filenames []string, tfParser string) (Linter, error) {\n\tassertion.Debugf(\"Filenames to scan: %v\\n\", filenames)\n\tswitch ruleSet.Type {\n\tcase \"Kubernetes\":\n\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: KubernetesResourceLoader{}}, nil\n\tcase \"Terraform\":\n\t\tif tfParser == \"tf11\" {\n\t\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: TerraformResourceLoader{}}, nil\n\t\t} else {\n\t\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: Terraform12ResourceLoader{}}, nil\n\t\t}\n\tcase \"Terraform12\":\n\t\tif tfParser == \"tf11\" {\n\t\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: TerraformResourceLoader{}}, nil\n\t\t} else {\n\t\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: Terraform12ResourceLoader{}}, nil\n\t\t}\n\tcase \"LintRules\":\n\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: RulesResourceLoader{}}, nil\n\tcase \"YAML\":\n\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: YAMLResourceLoader{Resources: ruleSet.Resources}}, nil\n\tcase \"JSON\":\n\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: JSONResourceLoader{Resources: ruleSet.Resources}}, nil\n\tcase \"CSV\":\n\t\treturn FileLinter{Filenames: filenames, ValueSource: vs, Loader: CSVResourceLoader{Columns: ruleSet.Columns}}, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"Type not supported: %s\", ruleSet.Source)\n\t}\n}\n"
  },
  {
    "path": "linter/linter_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"reflect\"\n\t\"testing\"\n)\n\ntype NewLinterTestCase struct {\n\tFilename string\n\tTypeName string\n}\n\nfunc TestNewLinter(t *testing.T) {\n\n\ttestCases := []NewLinterTestCase{\n\t\t{\"./testdata/rules/terraform_instance.yml\", \"FileLinter\"},\n\t\t{\"./testdata/rules/generic-yaml.yml\", \"FileLinter\"},\n\t\t{\"./testdata/rules/generic-json.yml\", \"FileLinter\"},\n\t\t{\"./testdata/rules/generic-csv.yml\", \"FileLinter\"},\n\t\t{\"./testdata/rules/kubernetes.yml\", \"FileLinter\"},\n\t\t{\"./testdata/rules/rules.yml\", \"FileLinter\"},\n\t}\n\n\tvs := MockValueSource{}\n\tfor _, tc := range testCases {\n\t\ttfParserOptions := []string{\"\", \"tf11\", \"tf12\"}\n\t\truleSet := loadRulesForTest(tc.Filename, t)\n\t\tfor _, tfPO := range tfParserOptions {\n\t\t\tl, err := NewLinter(ruleSet, vs, []string{}, tfPO)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expecting TestNewLinter to not return an error: %s\", err.Error())\n\t\t\t}\n\t\t\tn := reflect.TypeOf(l).Name()\n\t\t\tif n != tc.TypeName {\n\t\t\t\tt.Errorf(\"Expecting NewLinter expected %s, not %s \", tc.TypeName, n)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestUnknownLinterType(t *testing.T) {\n\truleSet := loadRulesForTest(\"./testdata/rules/unknown.yml\", t)\n\tvs := MockValueSource{}\n\ttfParserOptions := []string{\"\", \"tf11\", \"tf12\"}\n\tfor _, tfPO := range tfParserOptions {\n\t\t_, err := NewLinter(ruleSet, vs, []string{}, tfPO)\n\t\tif err == nil {\n\t\t\tt.Errorf(\"Expecting NewLinter to return an error for unsupported type\")\n\t\t}\n\t}\n}\n\ntype MockValueSource struct{}\n\nfunc (m MockValueSource) GetValue(e assertion.Expression) (string, error) {\n\treturn \"\", nil\n}\n"
  },
  {
    "path": "linter/resource_linter.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\n// ResourceLinter provides the basic validation logic used by all linters\ntype ResourceLinter struct {\n\tValueSource assertion.ValueSource\n}\n\n// ValidateResources evaluates a list of Rule objects to a list of Resource objects\nfunc (r ResourceLinter) ValidateResources(resources []assertion.Resource, rules []assertion.Rule) (assertion.ValidationReport, error) {\n\n\treport := assertion.ValidationReport{\n\t\tResourcesScanned: []assertion.ScannedResource{},\n\t\tViolations:       []assertion.Violation{},\n\t}\n\n\tresolvedRules, violations := assertion.ResolveRules(rules, r.ValueSource)\n\treport.Violations = append(report.Violations, violations...)\n\texternalRules := assertion.StandardExternalRuleInvoker{}\n\n\tfor _, rule := range resolvedRules {\n\t\tassertion.Debugf(\"Rule: ID: %v Message: %s\\n\", rule.ID, rule.Message)\n\t\tfilteredResources := assertion.FilterResourcesForRule(resources, rule)\n\t\tif len(filteredResources) == 0 {\n\t\t\tassertion.Debugf(\"No resources to check\\n\")\n\t\t}\n\t\tfor _, resource := range filteredResources {\n\t\t\tif assertion.ExcludeResource(rule, resource) {\n\t\t\t\tassertion.Debugf(\"Ignoring resource %s\\n\", resource.ID)\n\t\t\t} else {\n\t\t\t\tassertion.Debugf(\"Checking resource %s\\n\", resource.ID)\n\t\t\t\tstatus, violations, err := assertion.CheckRule(rule, resource, externalRules)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn report, nil\n\t\t\t\t}\n\t\t\t\treport.ResourcesScanned = append(report.ResourcesScanned, assertion.ScannedResource{\n\t\t\t\t\tResourceID:   resource.ID,\n\t\t\t\t\tResourceType: resource.Type,\n\t\t\t\t\tRuleID:       rule.ID,\n\t\t\t\t\tStatus:       status,\n\t\t\t\t\tFilename:     resource.Filename,\n\t\t\t\t\tLineNumber:   resource.LineNumber,\n\t\t\t\t})\n\t\t\t\treport.Violations = append(report.Violations, violations...)\n\t\t\t}\n\t\t}\n\t}\n\treturn report, nil\n}\n"
  },
  {
    "path": "linter/resource_linter_test.go",
    "content": "package linter\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIgnoreResource(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\tfilenames := []string{\"./testdata/resources/terraform_instance.tf\"}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: TerraformResourceLoader{}}\n\truleSet := loadRulesForTest(\"./testdata/rules/exclude_resource.yml\", t)\n\treport, err := linter.Validate(ruleSet, options)\n\tif err != nil {\n\t\tt.Error(\"Expecting TestIgnoreResource to not return an error\")\n\t}\n\tif len(report.ResourcesScanned) != 0 {\n\t\tt.Errorf(\"TestIgnoreResource scanned %d resources, expecting 0\", len(report.ResourcesScanned))\n\t}\n}\n"
  },
  {
    "path": "linter/rules_resource_loader.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"path/filepath\"\n)\n\n// RulesResourceLoader converts a YAML configuration file into a collection with Resource objects\ntype RulesResourceLoader struct{}\n\nfunc getAttr(m map[string]interface{}, keys ...string) []interface{} {\n\tfor _, key := range keys {\n\t\tif r, ok := m[key].([]interface{}); ok {\n\t\t\treturn r\n\t\t}\n\t}\n\treturn []interface{}{}\n}\n\n// Load converts a text file into a collection of Resource objects\nfunc (l RulesResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: make([]assertion.Resource, 0),\n\t}\n\tyamlResources, err := loadYAML(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tfor _, ruleSet := range yamlResources {\n\t\tsetResource := assertion.Resource{\n\t\t\tID:         getResourceIDFromFilename(filename),\n\t\t\tType:       \"LintRuleSet\",\n\t\t\tProperties: ruleSet,\n\t\t\tFilename:   filename,\n\t\t}\n\t\tloaded.Resources = append(loaded.Resources, setResource)\n\t\t// The LintRuleSet resources already has an attribute called Rules\n\t\t// but also adding each of these rules as a separate LintRule resource\n\t\t// makes writing rules a lot simpler\n\t\tm := ruleSet.(map[string]interface{})\n\t\trules := getAttr(m, \"rules\", \"Rules\")\n\t\tfor _, rule := range rules {\n\t\t\tproperties := rule.(map[string]interface{})\n\t\t\tproperties[\"__file__\"] = filename\n\t\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\t\truleResource := assertion.Resource{\n\t\t\t\tID:         properties[\"id\"].(string),\n\t\t\t\tType:       \"LintRule\",\n\t\t\t\tProperties: properties,\n\t\t\t\tFilename:   filename,\n\t\t\t}\n\t\t\tloaded.Resources = append(loaded.Resources, ruleResource)\n\t\t}\n\t}\n\treturn loaded, nil\n}\n\n// PostLoad does no additional processing for a RulesResourceLoader\nfunc (l RulesResourceLoader) PostLoad(r FileResources) ([]assertion.Resource, error) {\n\treturn r.Resources, nil\n}\n"
  },
  {
    "path": "linter/rules_resource_loader_test.go",
    "content": "package linter\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestRulesLinterValidate(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\truleSet := loadRulesForTest(\"./testdata/rules/rules.yml\", t)\n\tfilenames := []string{\"./testdata/rules/rules.yml\"}\n\tloader := RulesResourceLoader{}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, 2, len(report.ResourcesScanned), \"Expecting Validate to scan 2 resources\")\n\tassert.Equal(t, 1, len(report.FilesScanned), \"Expecting Validate to scan 1 file\")\n\tassert.Equal(t, 0, len(report.Violations), \"Expecting Validate to find 0 violations\")\n}\n\nfunc TestRulesLinterSearch(t *testing.T) {\n\truleSet := loadRulesForTest(\"./testdata/rules/rules.yml\", t)\n\tfilenames := []string{\"./testdata/rules/rules.yml\"}\n\tloader := RulesResourceLoader{}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\tvar b bytes.Buffer\n\tlinter.Search(ruleSet, \"message\", &b)\n\tassert.Contains(t, b.String(), \"needs\", \"Expecting TestRulesLinterSearch to find string in output\")\n}\n"
  },
  {
    "path": "linter/schema.go",
    "content": "package linter\n\nimport \"github.com/hashicorp/hcl/v2\"\n\n// lifted from terraform 0.12 source\nvar terraformSchema = &hcl.BodySchema{\n\tBlocks: []hcl.BlockHeaderSchema{\n\t\t{\n\t\t\tType: \"terraform\",\n\t\t},\n\t\t{\n\t\t\tType:       \"provider\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"variable\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType: \"locals\",\n\t\t},\n\t\t{\n\t\t\tType:       \"output\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"module\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"resource\",\n\t\t\tLabelNames: []string{\"type\", \"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"data\",\n\t\t\tLabelNames: []string{\"type\", \"name\"},\n\t\t},\n\t},\n}\n"
  },
  {
    "path": "linter/terraform.go",
    "content": "package linter\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/ghodss/yaml\"\n\t\"github.com/hashicorp/hcl\"\n\t\"github.com/hashicorp/hcl/hcl/ast\"\n\t\"github.com/hashicorp/hcl/hcl/parser\"\n\t\"github.com/stelligent/config-lint/assertion\"\n)\n\ntype (\n\t// TerraformResourceLoader converts Terraform configuration files into JSON objects\n\tTerraformResourceLoader struct{}\n\n\t// TerraformLoadResult collects all the returns value for parsing an HCL string\n\tTerraformLoadResult struct {\n\t\tResources []interface{}\n\t\tData      []interface{}\n\t\tProviders []interface{}\n\t\tModules   []interface{}\n\t\tVariables []Variable\n\t\tAST       *ast.File\n\t}\n)\n\nfunc loadHCL(filename string) (TerraformLoadResult, error) {\n\tresult := TerraformLoadResult{\n\t\tResources: []interface{}{},\n\t\tData:      []interface{}{},\n\t\tProviders: []interface{}{},\n\t\tModules:   []interface{}{},\n\t\tVariables: []Variable{},\n\t}\n\ttemplate, err := ioutil.ReadFile(filename)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\tresult.AST, _ = parser.Parse(template)\n\n\tvar v interface{}\n\terr = hcl.Unmarshal([]byte(template), &v)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tjsonData, err := json.MarshalIndent(v, \"\", \"  \")\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tassertion.Debugf(\"LoadHCL: %s\\n\", string(jsonData))\n\n\tvar hclData interface{}\n\terr = yaml.Unmarshal(jsonData, &hclData)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tm := hclData.(map[string]interface{})\n\tresult.Variables = append(loadVariables(m[\"variable\"]), loadLocalVariables(m[\"locals\"])...)\n\tif m[\"resource\"] != nil {\n\t\tresult.Resources = append(result.Resources, m[\"resource\"].([]interface{})...)\n\t}\n\tif m[\"data\"] != nil {\n\t\tresult.Data = append(result.Data, m[\"data\"].([]interface{})...)\n\t}\n\tif m[\"provider\"] != nil {\n\t\tresult.Providers = append(result.Providers, m[\"provider\"].([]interface{})...)\n\t}\n\tif m[\"module\"] != nil {\n\t\tresult.Modules = append(result.Modules, m[\"module\"].([]interface{})...)\n\t}\n\tassertion.Debugf(\"LoadHCL Variables: %v\\n\", result.Variables)\n\treturn result, nil\n}\n\nfunc loadVariables(data interface{}) []Variable {\n\tvariables := []Variable{}\n\tif data == nil {\n\t\treturn variables\n\t}\n\tlist := data.([]interface{})\n\tfor _, entry := range list {\n\t\tm := entry.(map[string]interface{})\n\t\tfor key, resource := range m {\n\t\t\tvariables = append(variables, Variable{Name: \"var.\" + key, Value: getVariableValue(key, resource)})\n\t\t}\n\t}\n\treturn variables\n}\n\nfunc loadLocalVariables(data interface{}) []Variable {\n\tvariables := []Variable{}\n\tif data == nil {\n\t\treturn variables\n\t}\n\tlist := data.([]interface{})\n\tfor _, entry := range list {\n\t\tm := entry.(map[string]interface{})\n\t\tfor key, value := range m {\n\t\t\tvariables = append(variables, Variable{Name: \"local.\" + key, Value: value})\n\t\t}\n\t}\n\treturn variables\n\n}\n\nfunc getVariableValue(key string, resource interface{}) interface{} {\n\tvalue := getVariableFromEnvironment(key)\n\tif value != \"\" {\n\t\treturn value\n\t}\n\treturn getVariableDefault(resource)\n}\n\nfunc getVariableFromEnvironment(key string) interface{} {\n\treturn os.Getenv(\"TF_VAR_\" + key)\n}\n\nfunc getVariableDefault(resource interface{}) interface{} {\n\tif list, ok := resource.([]interface{}); ok {\n\t\tvar defaultValue interface{}\n\t\tfor _, entry := range list {\n\t\t\tm := entry.(map[string]interface{})\n\t\t\tdefaultValue = m[\"default\"]\n\t\t}\n\t\treturn flattenMaps(defaultValue)\n\t}\n\treturn \"\"\n}\n\nfunc flattenMaps(v interface{}) interface{} {\n\t// map values are wrapped in an array, WAT?\n\tif listValue, ok := v.([]interface{}); ok {\n\t\tif len(listValue) == 1 {\n\t\t\tif mapValue, ok := listValue[0].(map[string]interface{}); ok {\n\t\t\t\treturn mapValue\n\t\t\t}\n\t\t}\n\t}\n\treturn v\n}\n\nfunc getResourceLineNumber(resourceType, resourceID, filename string, root *ast.File) int {\n\tresourceItems := root.Node.(*ast.ObjectList).Filter(\"resource\", resourceType, resourceID).Items\n\tif len(resourceItems) > 0 {\n\t\tresourcePos := resourceItems[0].Val.Pos()\n\t\tassertion.Debugf(\"Position %s %s:%d\\n\", resourceID, filename, resourcePos.Line)\n\t\treturn resourcePos.Line\n\t}\n\treturn 0\n}\n\n// Load parses an HCL file into a collection or Resource objects\nfunc (l TerraformResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: []assertion.Resource{},\n\t}\n\tresult, err := loadHCL(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tloaded.Variables = result.Variables\n\tloaded.Resources = append(loaded.Resources, getResources(filename, result.AST, result.Resources, \"resource\")...)\n\tloaded.Resources = append(loaded.Resources, getResources(filename, result.AST, result.Data, \"data\")...)\n\tloaded.Resources = append(loaded.Resources, getResources(filename, result.AST, addIDToProviders(result.Providers), \"provider\")...)\n\tloaded.Resources = append(loaded.Resources, getResources(filename, result.AST, addKeyToModules(result.Modules), \"module\")...)\n\n\tassertion.DebugJSON(\"loaded.Resources\", loaded.Resources)\n\n\treturn loaded, nil\n}\n\n// Providers do not have an name, so generate one to make the data format the same as resources\n\nfunc addIDToProviders(providers []interface{}) []interface{} {\n\tresources := []interface{}{}\n\tfor _, provider := range providers {\n\t\tresources = append(resources, addIDToProvider(provider))\n\t}\n\treturn resources\n}\n\nfunc addIDToProvider(provider interface{}) interface{} {\n\tm := provider.(map[string]interface{})\n\tfor providerType, value := range m {\n\t\tm[providerType] = addIDToProviderValue(value)\n\t}\n\treturn m\n}\n\n// Counter used to generate an ID for providers\nvar Counter = 0\n\nfunc addIDToProviderValue(value interface{}) interface{} {\n\tCounter++\n\tm := map[string]interface{}{}\n\tkey := fmt.Sprintf(\"%d\", Counter)\n\tm[key] = value\n\treturn []interface{}{m}\n}\n\n// use the source attribute of modules as the key\nfunc addKeyToModules(modules []interface{}) []interface{} {\n\tresources := map[string]interface{}{}\n\tfor _, module := range modules {\n\t\tresources = addKeyToModule(resources, module)\n\t}\n\treturn []interface{}{resources}\n}\n\nfunc addKeyToModule(resources map[string]interface{}, module interface{}) map[string]interface{} {\n\tm := module.(map[string]interface{})\n\tfor moduleName, valueList := range m {\n\t\ta := valueList.([]interface{})\n\t\tfor _, value := range a {\n\t\t\tproperties := value.(map[string]interface{})\n\t\t\tsource := properties[\"source\"].(string)\n\n\t\t\tinner := []interface{}{properties}\n\n\t\t\touter := map[string]interface{}{}\n\t\t\touter[moduleName] = inner\n\n\t\t\texisting, ok := resources[source]\n\t\t\tif ok {\n\t\t\t\tlist := existing.([]interface{})\n\t\t\t\tresources[source] = append(list, outer)\n\t\t\t} else {\n\t\t\t\tresources[source] = []interface{}{outer}\n\t\t\t}\n\t\t}\n\t}\n\treturn resources\n}\n\nfunc getResources(filename string, ast *ast.File, objects []interface{}, category string) []assertion.Resource {\n\tresources := []assertion.Resource{}\n\tfor _, resource := range objects {\n\t\tfor resourceType, templateResources := range resource.(map[string]interface{}) {\n\t\t\tif templateResources != nil {\n\t\t\t\tfor _, templateResource := range templateResources.([]interface{}) {\n\t\t\t\t\tfor resourceID, templateResource := range templateResource.(map[string]interface{}) {\n\t\t\t\t\t\tproperties := getProperties(templateResource)\n\t\t\t\t\t\tlineNumber := getResourceLineNumber(resourceType, resourceID, filename, ast)\n\t\t\t\t\t\t// Expose block ID so it could be linted e.g.\n\t\t\t\t\t\t// resource \"aws_s3_bucket\" \"web\" { ... }\n\t\t\t\t\t\t// The block name/id here is \"web\".\n\t\t\t\t\t\tproperties[\"__name__\"] = resourceID\n\t\t\t\t\t\tproperties[\"__file__\"] = filename\n\t\t\t\t\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\t\t\t\t\ttr := assertion.Resource{\n\t\t\t\t\t\t\tID:         resourceID,\n\t\t\t\t\t\t\tType:       resourceType,\n\t\t\t\t\t\t\tCategory:   category,\n\t\t\t\t\t\t\tProperties: properties,\n\t\t\t\t\t\t\tFilename:   filename,\n\t\t\t\t\t\t\tLineNumber: lineNumber,\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresources = append(resources, tr)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn resources\n}\n\n// PostLoad resolves variable expressions\nfunc (l TerraformResourceLoader) PostLoad(fr FileResources) ([]assertion.Resource, error) {\n\tfor _, resource := range fr.Resources {\n\t\tresource.Properties = replaceVariables(resource.Properties, fr.Variables)\n\t}\n\tfor _, resource := range fr.Resources {\n\t\tproperties, err := parseJSONDocuments(resource.Properties)\n\t\tif err != nil {\n\t\t\treturn fr.Resources, err\n\t\t}\n\t\tresource.Properties = properties\n\t}\n\treturn fr.Resources, nil\n}\n\nfunc replaceVariables(templateResource interface{}, variables []Variable) interface{} {\n\tswitch v := templateResource.(type) {\n\tcase map[string]interface{}:\n\t\treturn replaceVariablesInMap(v, variables)\n\tcase string:\n\t\treturn interpolate(v, variables)\n\tdefault:\n\t\tassertion.Debugf(\"replaceVariables cannot process type %T: %v\\n\", v, v)\n\t\treturn templateResource\n\t}\n}\n\nfunc replaceVariablesInMap(templateResource map[string]interface{}, variables []Variable) interface{} {\n\tfor key, value := range templateResource {\n\t\tswitch v := value.(type) {\n\t\tcase string:\n\t\t\ttemplateResource[key] = interpolate(v, variables)\n\t\tcase map[string]interface{}:\n\t\t\ttemplateResource[key] = replaceVariablesInMap(v, variables)\n\t\tcase []interface{}:\n\t\t\ttemplateResource[key] = replaceVariablesInList(v, variables)\n\t\tdefault:\n\t\t\tassertion.Debugf(\"replaceVariablesInMap cannot process type %T\\n\", v)\n\t\t}\n\t}\n\treturn templateResource\n}\n\nfunc replaceVariablesInList(list []interface{}, variables []Variable) []interface{} {\n\tresult := []interface{}{}\n\tfor _, e := range list {\n\t\tresult = append(result, replaceVariables(e, variables))\n\t}\n\treturn result\n}\n\nfunc parseJSONDocuments(resource interface{}) (interface{}, error) {\n\tproperties := resource.(map[string]interface{})\n\tfor _, attribute := range []string{\"assume_role_policy\", \"policy\", \"container_definitions\", \"access_policies\", \"access_policy\", \"container_properties\"} {\n\t\tif policyAttribute, hasPolicyString := properties[attribute]; hasPolicyString {\n\t\t\tif policyString, isString := policyAttribute.(string); isString {\n\t\t\t\tvar policy interface{}\n\t\t\t\tif policyString != \"\" && policyString != \"UNDEFINED\" {\n\t\t\t\t\terr := json.Unmarshal([]byte(policyString), &policy)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tassertion.Debugf(\"Unable to parse '%s' as JSON\\n\", policyString)\n\t\t\t\t\t\tassertion.Debugf(\"Error: %v\\n\", err)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tproperties[attribute] = policy\n\t\t\t}\n\t\t}\n\t}\n\treturn properties, nil\n}\n\nfunc getProperties(templateResource interface{}) map[string]interface{} {\n\tswitch v := templateResource.(type) {\n\tcase []interface{}:\n\t\treturn v[0].(map[string]interface{})\n\tdefault:\n\t\treturn map[string]interface{}{}\n\t}\n}\n"
  },
  {
    "path": "linter/terraform_interpolate.go",
    "content": "package linter\n\nimport (\n\t\"fmt\"\n\t\"github.com/hashicorp/hil\"\n\t\"github.com/hashicorp/hil/ast\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"io/ioutil\"\n\t\"regexp\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc makeList(variables []interface{}) []ast.Variable {\n\tlist := []ast.Variable{}\n\tfor _, v := range variables {\n\t\tlist = append(list, makeVar(v))\n\t}\n\treturn list\n}\n\nfunc makeMap(m map[string]interface{}) map[string]ast.Variable {\n\tresult := map[string]ast.Variable{}\n\tfor k, v := range m {\n\t\tif stringValue, ok := v.(string); ok {\n\t\t\tresult[k] = ast.Variable{Type: ast.TypeString, Value: stringValue}\n\t\t}\n\t}\n\treturn result\n}\n\nfunc makeVar(v interface{}) ast.Variable {\n\tswitch tv := v.(type) {\n\tcase string:\n\t\treturn ast.Variable{\n\t\t\tType:  ast.TypeString,\n\t\t\tValue: tv,\n\t\t}\n\tcase []interface{}:\n\t\treturn ast.Variable{\n\t\t\tType:  ast.TypeList,\n\t\t\tValue: makeList(tv),\n\t\t}\n\tcase map[string]interface{}:\n\t\tm := ast.Variable{\n\t\t\tType:  ast.TypeMap,\n\t\t\tValue: makeMap(tv),\n\t\t}\n\t\treturn m\n\tdefault:\n\t\treturn ast.Variable{\n\t\t\tType:  ast.TypeString,\n\t\t\tValue: \"\",\n\t\t}\n\t}\n}\n\nfunc makeVarMap(variables []Variable) map[string]ast.Variable {\n\tm := map[string]ast.Variable{}\n\tfor _, v := range variables {\n\t\tm[v.Name] = makeVar(v.Value)\n\t}\n\treturn m\n}\n\nfunc interpolationFuncFile() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:   []ast.Type{ast.TypeString},\n\t\tReturnType: ast.TypeString,\n\t\tVariadic:   false,\n\t\tCallback: func(inputs []interface{}) (interface{}, error) {\n\t\t\tb, err := ioutil.ReadFile(inputs[0].(string))\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", nil\n\t\t\t}\n\t\t\treturn strings.TrimSpace(string(b)), nil\n\t\t},\n\t}\n}\n\n// The following functions are copied or adapted\n// from https://github.com/hashicorp/terraform/blob/master/config/interpolate_funcs.go\n\n// interpolationFuncLookup implements the \"lookup\" function that allows\n// dynamic lookups of map types within a Terraform configuration.\nfunc interpolationFuncLookup() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{ast.TypeMap, ast.TypeString},\n\t\tReturnType:   ast.TypeString,\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeString,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tdefaultValue := \"\"\n\t\t\tdefaultValueSet := false\n\t\t\tif len(args) > 2 {\n\t\t\t\tdefaultValue = args[2].(string)\n\t\t\t\tdefaultValueSet = true\n\t\t\t}\n\t\t\tif len(args) > 3 {\n\t\t\t\treturn \"\", fmt.Errorf(\"lookup() takes no more than three arguments\")\n\t\t\t}\n\t\t\tindex := args[1].(string)\n\t\t\tmapVar := args[0].(map[string]ast.Variable)\n\n\t\t\tv, ok := mapVar[index]\n\t\t\tif !ok {\n\t\t\t\tif defaultValueSet {\n\t\t\t\t\treturn defaultValue, nil\n\t\t\t\t} else {\n\t\t\t\t\treturn \"\", fmt.Errorf(\n\t\t\t\t\t\t\"lookup failed to find '%s'\",\n\t\t\t\t\t\targs[1].(string))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif v.Type != ast.TypeString {\n\t\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\t\"lookup() may only be used with flat maps, this map contains elements of %s\",\n\t\t\t\t\tv.Type.Printable())\n\t\t\t}\n\n\t\t\treturn v.Value.(string), nil\n\t\t},\n\t}\n}\n\n// interpolationFuncJoin implements the \"join\" function that allows\n// multi-variable values to be joined by some character.\nfunc interpolationFuncJoin() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{ast.TypeString},\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeList,\n\t\tReturnType:   ast.TypeString,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tvar list []string\n\n\t\t\tif len(args) < 2 {\n\t\t\t\treturn nil, fmt.Errorf(\"not enough arguments to join()\")\n\t\t\t}\n\n\t\t\tfor _, arg := range args[1:] {\n\t\t\t\tfor _, part := range arg.([]ast.Variable) {\n\t\t\t\t\tif part.Type != ast.TypeString {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\t\t\t\"only works on flat lists, this list contains elements of %s\",\n\t\t\t\t\t\t\tpart.Type.Printable())\n\t\t\t\t\t}\n\t\t\t\t\tlist = append(list, part.Value.(string))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn strings.Join(list, args[0].(string)), nil\n\t\t},\n\t}\n}\n\n// interpolationFuncConcat implements the \"concat\" function that concatenates\n// multiple lists.\nfunc interpolationFuncConcat() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{ast.TypeList},\n\t\tReturnType:   ast.TypeList,\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeList,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tvar outputList []ast.Variable\n\n\t\t\tfor _, arg := range args {\n\t\t\t\tfor _, v := range arg.([]ast.Variable) {\n\t\t\t\t\tswitch v.Type {\n\t\t\t\t\tcase ast.TypeString:\n\t\t\t\t\t\toutputList = append(outputList, v)\n\t\t\t\t\tcase ast.TypeList:\n\t\t\t\t\t\toutputList = append(outputList, v)\n\t\t\t\t\tcase ast.TypeMap:\n\t\t\t\t\t\toutputList = append(outputList, v)\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"concat() does not support lists of %s\", v.Type.Printable())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// we don't support heterogeneous types, so make sure all types match the first\n\t\t\tif len(outputList) > 0 {\n\t\t\t\tfirstType := outputList[0].Type\n\t\t\t\tfor _, v := range outputList[1:] {\n\t\t\t\t\tif v.Type != firstType {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"unexpected %s in list of %s\", v.Type.Printable(), firstType.Printable())\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn outputList, nil\n\t\t},\n\t}\n}\n\n// interpolationFuncFormat implements the \"format\" function that does\n// string formatting.\nfunc interpolationFuncFormat() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{ast.TypeString},\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeAny,\n\t\tReturnType:   ast.TypeString,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tformat := args[0].(string)\n\t\t\treturn fmt.Sprintf(format, args[1:]...), nil\n\t\t},\n\t}\n}\n\n// interpolationFuncList creates a list from the parameters passed\n// to it.\nfunc interpolationFuncList() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{},\n\t\tReturnType:   ast.TypeList,\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeAny,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tvar outputList []ast.Variable\n\n\t\t\tfor i, val := range args {\n\t\t\t\tswitch v := val.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\toutputList = append(outputList, ast.Variable{Type: ast.TypeString, Value: v})\n\t\t\t\tcase []ast.Variable:\n\t\t\t\t\toutputList = append(outputList, ast.Variable{Type: ast.TypeList, Value: v})\n\t\t\t\tcase map[string]ast.Variable:\n\t\t\t\t\toutputList = append(outputList, ast.Variable{Type: ast.TypeMap, Value: v})\n\t\t\t\tdefault:\n\t\t\t\t\treturn nil, fmt.Errorf(\"unexpected type %T for argument %d in list\", v, i)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// we don't support heterogeneous types, so make sure all types match the first\n\t\t\tif len(outputList) > 0 {\n\t\t\t\tfirstType := outputList[0].Type\n\t\t\t\tfor i, v := range outputList[1:] {\n\t\t\t\t\tif v.Type != firstType {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"unexpected type %s for argument %d in list\", v.Type, i+1)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn outputList, nil\n\t\t},\n\t}\n}\n\n// interpolationFuncReplace implements the \"replace\" function that does\n// string replacement.\nfunc interpolationFuncReplace() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:   []ast.Type{ast.TypeString, ast.TypeString, ast.TypeString},\n\t\tReturnType: ast.TypeString,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\ts := args[0].(string)\n\t\t\tsearch := args[1].(string)\n\t\t\treplace := args[2].(string)\n\n\t\t\t// We search/replace using a regexp if the string is surrounded\n\t\t\t// in forward slashes.\n\t\t\tif len(search) > 1 && search[0] == '/' && search[len(search)-1] == '/' {\n\t\t\t\tre, err := regexp.Compile(search[1 : len(search)-1])\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\n\t\t\t\treturn re.ReplaceAllString(s, replace), nil\n\t\t\t}\n\n\t\t\treturn strings.Replace(s, search, replace, -1), nil\n\t\t},\n\t}\n}\n\n// interpolationFuncElement implements the \"element\" function that allows\n// a specific index to be looked up in a multi-variable value. Note that this will\n// wrap if the index is larger than the number of elements in the multi-variable value.\nfunc interpolationFuncElement() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:   []ast.Type{ast.TypeList, ast.TypeString},\n\t\tReturnType: ast.TypeString,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\tlist := args[0].([]ast.Variable)\n\t\t\tif len(list) == 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"element() may not be used with an empty list\")\n\t\t\t}\n\n\t\t\tindex, err := strconv.Atoi(args[1].(string))\n\t\t\tif err != nil || index < 0 {\n\t\t\t\treturn \"\", fmt.Errorf(\n\t\t\t\t\t\"invalid number for index, got %s\", args[1])\n\t\t\t}\n\n\t\t\tresolvedIndex := index % len(list)\n\n\t\t\tv := list[resolvedIndex]\n\t\t\tif v.Type != ast.TypeString {\n\t\t\t\treturn nil, fmt.Errorf(\n\t\t\t\t\t\"element() may only be used with flat lists, this list contains elements of %s\",\n\t\t\t\t\tv.Type.Printable())\n\t\t\t}\n\t\t\treturn v.Value, nil\n\t\t},\n\t}\n}\n\n// interpolationFuncMap creates a map from the parameters passed\n// to it.\nfunc interpolationFuncMap() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{},\n\t\tReturnType:   ast.TypeMap,\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeAny,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\toutputMap := make(map[string]ast.Variable)\n\n\t\t\tif len(args)%2 != 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"requires an even number of arguments, got %d\", len(args))\n\t\t\t}\n\n\t\t\tvar firstType *ast.Type\n\t\t\tfor i := 0; i < len(args); i += 2 {\n\t\t\t\tkey, ok := args[i].(string)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"argument %d represents a key, so it must be a string\", i+1)\n\t\t\t\t}\n\t\t\t\tval := args[i+1]\n\t\t\t\tvariable, err := hil.InterfaceToVariable(val)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\t// Enforce map type homogeneity\n\t\t\t\tif firstType == nil {\n\t\t\t\t\tfirstType = &variable.Type\n\t\t\t\t} else if variable.Type != *firstType {\n\t\t\t\t\treturn nil, fmt.Errorf(\"all map values must have the same type, got %s then %s\", firstType.Printable(), variable.Type.Printable())\n\t\t\t\t}\n\t\t\t\t// Check for duplicate keys\n\t\t\t\tif _, ok := outputMap[key]; ok {\n\t\t\t\t\treturn nil, fmt.Errorf(\"argument %d is a duplicate key: %q\", i+1, key)\n\t\t\t\t}\n\t\t\t\toutputMap[key] = variable\n\t\t\t}\n\n\t\t\treturn outputMap, nil\n\t\t},\n\t}\n}\n\nfunc interpolationFuncMerge() ast.Function {\n\treturn ast.Function{\n\t\tArgTypes:     []ast.Type{ast.TypeMap},\n\t\tReturnType:   ast.TypeMap,\n\t\tVariadic:     true,\n\t\tVariadicType: ast.TypeMap,\n\t\tCallback: func(args []interface{}) (interface{}, error) {\n\t\t\toutputMap := make(map[string]ast.Variable)\n\n\t\t\tfor _, arg := range args {\n\t\t\t\tfor k, v := range arg.(map[string]ast.Variable) {\n\t\t\t\t\toutputMap[k] = v\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn outputMap, nil\n\t\t},\n\t}\n}\n\nfunc Funcs() map[string]ast.Function {\n\treturn map[string]ast.Function{\n\t\t\"concat\":  interpolationFuncConcat(),\n\t\t\"element\": interpolationFuncElement(),\n\t\t\"file\":    interpolationFuncFile(),\n\t\t\"format\":  interpolationFuncFormat(),\n\t\t\"join\":    interpolationFuncJoin(),\n\t\t\"list\":    interpolationFuncList(),\n\t\t\"lookup\":  interpolationFuncLookup(),\n\t\t\"map\":     interpolationFuncMap(),\n\t\t\"merge\":   interpolationFuncMerge(),\n\t\t\"replace\": interpolationFuncReplace(),\n\t}\n}\n\nfunc interpolate(s string, variables []Variable) interface{} {\n\tif strings.Index(s, \"${\") == -1 {\n\t\t// no interpolation to be done\n\t\treturn s\n\t}\n\tassertion.Debugf(\"interpolate: %s\\n\", s)\n\tconfig := &hil.EvalConfig{\n\t\tGlobalScope: &ast.BasicScope{\n\t\t\tVarMap:  makeVarMap(variables),\n\t\t\tFuncMap: Funcs(),\n\t\t},\n\t}\n\ttree, err := hil.Parse(s)\n\tif err != nil {\n\t\tassertion.Debugf(\"Parse error: %v\\n\", err)\n\t\treturn s\n\t}\n\tresult, err := hil.Eval(tree, config)\n\tif err != nil {\n\t\tassertion.Debugf(\"Eval error: %v\\n\", err)\n\t\treturn s\n\t}\n\tassertion.Debugf(\"interpolation result: %v\\n\", result.Value)\n\tif stringValue, ok := result.Value.(string); ok {\n\t\tif stringValue == s {\n\t\t\treturn stringValue // no changes, no need for recursive call\n\t\t}\n\t\treturn interpolate(stringValue, variables)\n\t}\n\treturn result.Value\n}\n"
  },
  {
    "path": "linter/terraform_interpolate_test.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\ntype interpolationTestCase struct {\n\tInput    string\n\tExpected interface{}\n}\n\nfunc TestInterpolation(t *testing.T) {\n\ttestCases := []interpolationTestCase{\n\t\t{\"${2+6}\", \"8\"},\n\t\t{\"bucket_${var.environment}\", \"bucket_development\"},\n\t\t{\"${var.environment == \\\"development\\\" ? \\\"YES\\\" : \\\"NO\\\"}\", \"YES\"},\n\t\t{\"${local.count + local.count}\", \"202\"},\n\t\t{\"${replace(var.template,var.user_pattern,var.user)}\", \"https://users/adam\"},\n\t\t{\"${list(var.a, var.b, var.c)}\", []interface{}{\"one\", \"two\", \"three\"}},\n\t\t{\"${element(list(var.a, var.b, var.c),2)}\", \"three\"},\n\t\t{\"${join(var.pipe, list(var.a, var.b))}\", \"one|two\"},\n\t\t{\"${concat(list(var.a,var.b), list(var.c))}\", []interface{}{\"one\", \"two\", \"three\"}},\n\t\t{\"${format(\\\"id-%s\\\",var.a)}\", \"id-one\"},\n\t\t{\"${map(var.k1,var.v1,var.k2,var.v2)}\", map[string]interface{}{\"key1\": \"value1\", \"key2\": \"value2\"}},\n\t\t{\"${missing_func(1)}\", \"${missing_func(1)}\"},\n\t\t{\"${module.required_tags.tags}\", \"${module.required_tags.tags}\"},\n\t\t{\"${merge(map(\\\"NodeType\\\", \\\"Runner\\\"), var.tags)}\", map[string]interface{}{\"NodeType\": \"Runner\", \"Name\": \"Web\"}},\n\t\t{\"{\\\"version\\\":\\\"$LATEST\\\"}\", \"{\\\"version\\\":\\\"$LATEST\\\"}\"},\n\t\t{\"echo $PWD\", \"echo $PWD\"},\n\t}\n\tvars := []Variable{\n\t\t{Name: \"var.environment\", Value: \"development\"},\n\t\t{Name: \"local.count\", Value: \"101\"},\n\t\t{Name: \"var.template\", Value: \"https://users/USER_ID\"},\n\t\t{Name: \"var.user_pattern\", Value: \"USER_ID\"},\n\t\t{Name: \"var.user\", Value: \"adam\"},\n\t\t{Name: \"var.a\", Value: \"one\"},\n\t\t{Name: \"var.b\", Value: \"two\"},\n\t\t{Name: \"var.c\", Value: \"three\"},\n\t\t{Name: \"var.pipe\", Value: \"|\"},\n\t\t{Name: \"var.k1\", Value: \"key1\"},\n\t\t{Name: \"var.k2\", Value: \"key2\"},\n\t\t{Name: \"var.v1\", Value: \"value1\"},\n\t\t{Name: \"var.v2\", Value: \"value2\"},\n\t\t{Name: \"var.tags\", Value: map[string]interface{}{\"Name\": \"Web\"}},\n\t}\n\tfor _, tc := range testCases {\n\t\tresult := interpolate(tc.Input, vars)\n\t\tassert.Equal(t, tc.Expected, result)\n\t}\n}\n"
  },
  {
    "path": "linter/terraform_test.go",
    "content": "package linter\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTerraformLinter(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\tfilenames := []string{\"./testdata/resources/terraform_instance.tf\"}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: TerraformResourceLoader{}}\n\truleSet := loadRulesForTest(\"./testdata/rules/terraform_instance.yml\", t)\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, len(report.ResourcesScanned), 1, \"Unexpected number of resources scanned\")\n\tassert.Equal(t, len(report.FilesScanned), 1, \"Unexpected number of files scanned\")\n\tassertViolationsCount(\"TestTerraformLinter \", 0, report.Violations, t)\n}\n\nfunc loadResourcesToTest(t *testing.T, filename string) []assertion.Resource {\n\tloader := TerraformResourceLoader{}\n\tloaded, err := loader.Load(filename)\n\tassert.Nil(t, err, \"Expecting Load to run without error\")\n\tresources, err := loader.PostLoad(loaded)\n\tassert.Nil(t, err, \"Expecting PostLoad to run without error\")\n\treturn resources\n}\n\nfunc getResourceTags(r assertion.Resource) map[string]interface{} {\n\tproperties := r.Properties.(map[string]interface{})\n\ttags := properties[\"tags\"].([]interface{})\n\treturn tags[0].(map[string]interface{})\n}\n\nfunc TestTerraformVariable(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"ami\"], \"ami-f2d3638a\", \"Unexpected value for simple variable\")\n}\n\nfunc TestTerraformVariableWithNoDefault(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, tags[\"department\"], \"\", \"Unexpected value for variable with no default\")\n}\n\nfunc TestTerraformFunctionCall(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, tags[\"environment\"], \"test\", \"Unexpected value for lookup function\")\n}\n\nfunc TestTerraformListVariable(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, tags[\"comment\"], \"bar\", \"Unexpected value for list variable\")\n}\n\nfunc TestTerraformLocalVariable(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_local_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, \"myprojectbucket\", properties[\"name\"], \"Unexpected value for name attribute\")\n}\n\nfunc TestTerraformVariablesFromEnvironment(t *testing.T) {\n\tos.Setenv(\"TF_VAR_instance_type\", \"c4.large\")\n\tresources := loadResourcesToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"instance_type\"], \"c4.large\", \"Unexpected value for instance_type\")\n\tos.Setenv(\"TF_VAR_instance_type\", \"\")\n}\n\nfunc TestTerraformFileFunction(t *testing.T) {\n\tresources := loadResourcesToTest(t, \"./testdata/resources/reference_file.tf\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"bucket\"], \"example\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraformVariablesInDifferentFile(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\tfilenames := []string{\n\t\t\"./testdata/resources/defines_variables.tf\",\n\t\t\"./testdata/resources/reference_variables.tf\",\n\t}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: TerraformResourceLoader{}}\n\truleSet := loadRulesForTest(\"./testdata/rules/terraform_instance.yml\", t)\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, len(report.ResourcesScanned), 1, \"Unexpected number of resources\")\n\tassert.Equal(t, len(report.FilesScanned), 2, \"Unexpected number of files scanned\")\n\tassertViolationsCount(\"TestTerraformVariablesInDifferentFile \", 0, report.Violations, t)\n}\n\ntype TestingValueSource struct{}\n\nfunc (s TestingValueSource) GetValue(a assertion.Expression) (string, error) {\n\tif a.ValueFrom.URL != \"\" {\n\t\treturn \"TEST\", nil\n\t}\n\treturn a.Value, nil\n}\n\nfunc TestTerraformDataLoader(t *testing.T) {\n\tloader := TerraformResourceLoader{}\n\tloaded, err := loader.Load(\"./testdata/resources/terraform_data.tf\")\n\tassert.Nil(t, err, \"Expecting Load to run without error\")\n\tassert.Equal(t, len(loaded.Resources), 1, \"Unexpected number of resources\")\n}\n\ntype terraformLinterTestCase struct {\n\tConfigurationFilename   string\n\tRulesFilename           string\n\tExpectedViolationCount  int\n\tExpectedViolationRuleID string\n}\n\nfunc TestTerraformLinterCases(t *testing.T) {\n\ttestCases := map[string]terraformLinterTestCase{\n\t\t\"ParseError\": {\n\t\t\t\"./testdata/resources/terraform_syntax_error.tf\",\n\t\t\t\"./testdata/rules/terraform_provider.yml\",\n\t\t\t1,\n\t\t\t\"FILE_LOAD\",\n\t\t},\n\t\t\"Provider\": {\n\t\t\t\"./testdata/resources/terraform_provider.tf\",\n\t\t\t\"./testdata/rules/terraform_provider.yml\",\n\t\t\t1,\n\t\t\t\"AWS_PROVIDER\",\n\t\t},\n\t\t\"DataObject\": {\n\t\t\t\"./testdata/resources/terraform_data.tf\",\n\t\t\t\"./testdata/rules/terraform_data.yml\",\n\t\t\t1,\n\t\t\t\"DATA_NOT_CONTAINS\",\n\t\t},\n\t\t\"PoliciesWithVariables\": {\n\t\t\t\"./testdata/resources/policy_with_variables.tf\",\n\t\t\t\"./testdata/rules/policy_variable.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"HereDocWithExpression\": {\n\t\t\t\"./testdata/resources/policy_with_expression.tf\",\n\t\t\t\"./testdata/rules/policy_variable.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"Policies\": {\n\t\t\t\"./testdata/resources/terraform_policy.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t1,\n\t\t\t\"TEST_POLICY\",\n\t\t},\n\t\t\"PolicyInvalidJSON\": {\n\t\t\t\"./testdata/resources/terraform_policy_invalid_json.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"PolicyEmpty\": {\n\t\t\t\"./testdata/resources/terraform_policy_empty.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"Module\": {\n\t\t\t\"./testdata/resources/terraform_module.tf\",\n\t\t\t\"./testdata/rules/terraform_module.yml\",\n\t\t\t1,\n\t\t\t\"MODULE_DESCRIPTION\",\n\t\t},\n\t\t\"BatchPrivileged\": {\n\t\t\t\"./testdata/resources/batch_privileged.tf\",\n\t\t\t\"./testdata/rules/batch_definition.yml\",\n\t\t\t1,\n\t\t\t\"BATCH_DEFINITION_PRIVILEGED\",\n\t\t},\n\t\t\"CloudfrontAccessLogs\": {\n\t\t\t\"./testdata/resources/cloudfront_access_logs.tf\",\n\t\t\t\"./testdata/rules/cloudfront_access_logs.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"PublicEC2\": {\n\t\t\t\"./testdata/resources/ec2_public.tf\",\n\t\t\t\"./testdata/rules/ec2_public.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"ElastiCacheRest\": {\n\t\t\t\"./testdata/resources/elasticache_encryption_rest.tf\",\n\t\t\t\"./testdata/rules/elasticache_encryption_rest.yml\",\n\t\t\t1,\n\t\t\t\"ELASTICACHE_ENCRYPTION_REST\",\n\t\t},\n\t\t\"ElastiCacheTransit\": {\n\t\t\t\"./testdata/resources/elasticache_encryption_transit.tf\",\n\t\t\t\"./testdata/rules/elasticache_encryption_transit.yml\",\n\t\t\t1,\n\t\t\t\"ELASTICACHE_ENCRYPTION_TRANSIT\",\n\t\t},\n\t\t\"NeptuneClusterEncryption\": {\n\t\t\t\"./testdata/resources/neptune_db_encryption.tf\",\n\t\t\t\"./testdata/rules/neptune_db_encryption.yml\",\n\t\t\t1,\n\t\t\t\"NEPTUNE_DB_ENCRYPTION\",\n\t\t},\n\t\t\"RdsPublic\": {\n\t\t\t\"./testdata/resources/rds_publicly_available.tf\",\n\t\t\t\"./testdata/rules/rds_publicly_available.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"KinesisKms\": {\n\t\t\t\"./testdata/resources/kinesis_kms_stream.tf\",\n\t\t\t\"./testdata/rules/kinesis_kms_stream.yml\",\n\t\t\t1,\n\t\t\t\"KINESIS_STREAM_KMS\",\n\t\t},\n\t\t\"DmsEncryption\": {\n\t\t\t\"./testdata/resources/dms_endpoint_encryption.tf\",\n\t\t\t\"./testdata/rules/dms_endpoint_encryption.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"EmrClusterLogs\": {\n\t\t\t\"./testdata/resources/emr_cluster_logs.tf\",\n\t\t\t\"./testdata/rules/emr_cluster_logs.yml\",\n\t\t\t1,\n\t\t\t\"AWS_EMR_CLUSTER_LOGGING\",\n\t\t},\n\t\t\"KmsKeyRotation\": {\n\t\t\t\"./testdata/resources/kms_key_rotation.tf\",\n\t\t\t\"./testdata/rules/kms_key_rotation.yml\",\n\t\t\t1,\n\t\t\t\"AWS_KMS_KEY_ROTATION\",\n\t\t},\n\t\t\"SagemakerEndpoint\": {\n\t\t\t\"./testdata/resources/sagemaker_endpoint_encryption.tf\",\n\t\t\t\"./testdata/rules/sagemaker_endpoint_encryption.yml\",\n\t\t\t1,\n\t\t\t\"SAGEMAKER_ENDPOINT_ENCRYPTION\",\n\t\t},\n\t\t\"SagemakerNotebook\": {\n\t\t\t\"./testdata/resources/sagemaker_notebook_encryption.tf\",\n\t\t\t\"./testdata/rules/sagemaker_notebook_encryption.yml\",\n\t\t\t1,\n\t\t\t\"SAGEMAKER_NOTEBOOK_ENCRYPTION\",\n\t\t},\n\t}\n\tfor name, tc := range testCases {\n\t\toptions := Options{\n\t\t\tTags:    []string{},\n\t\t\tRuleIDs: []string{},\n\t\t}\n\t\tfilenames := []string{tc.ConfigurationFilename}\n\t\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: TerraformResourceLoader{}}\n\t\truleSet := loadRulesForTest(tc.RulesFilename, t)\n\t\treport, err := linter.Validate(ruleSet, options)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Expecting %s to return without an error: %s\", name, err.Error())\n\t\t}\n\t\tif len(report.FilesScanned) != 1 {\n\t\t\tt.Errorf(\"TestTerraformLinterCases scanned %d files, expecting 1\", len(report.FilesScanned))\n\t\t}\n\t\tif len(report.Violations) != tc.ExpectedViolationCount {\n\t\t\tt.Errorf(\"%s returned %d violations, expecting %d\", name, len(report.Violations), tc.ExpectedViolationCount)\n\t\t\tt.Errorf(\"Violations: %v\", report.Violations)\n\t\t}\n\t\tif tc.ExpectedViolationRuleID != \"\" {\n\t\t\tassertViolationByRuleID(name, tc.ExpectedViolationRuleID, report.Violations, t)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "linter/terraform_v12.go",
    "content": "package linter\n\nimport (\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/hcl/v2\"\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stelligent/config-lint/linter/tf12parser\"\n\t\"github.com/zclconf/go-cty/cty\"\n)\n\ntype (\n\t// Terraform12ResourceLoader converts Terraform configuration files into JSON objects\n\tTerraform12ResourceLoader struct{}\n\n\t// Terraform12LoadResult collects all the returns value for parsing an HCL string\n\tTerraform12LoadResult struct {\n\t\tResources []assertion.Resource\n\t\tData      []interface{}\n\t\tProviders []interface{}\n\t\tModules   []interface{}\n\t\tVariables []Variable\n\t\tAST       *hcl.File\n\t}\n)\n\nvar (\n\tblockTypes = []string{\n\t\t\"data\",\n\t\t\"locals\",\n\t\t\"module\",\n\t\t\"output\",\n\t\t\"provider\",\n\t\t\"resource\",\n\t\t\"terraform\",\n\t\t\"variable\",\n\t}\n\n\tblockLabelSyntax = map[string][]string{\n\t\t\"TypeAndName\":  []string{\"data\", \"resource\"},\n\t\t\"TypeOnly\":     []string{\"provider\"},\n\t\t\"NameOnly\":     []string{\"module\", \"output\", \"variable\"},\n\t\t\"NoTypeNoName\": []string{\"locals\", \"terraform\"},\n\t}\n)\n\n// Load parses an HCLv2 file into a collection or Resource objects\n//TODO: This should be unused, but can't remove due to the interface, I think?\nfunc (l Terraform12ResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: []assertion.Resource{},\n\t}\n\tresult, err := loadHCLv2([]string{filename})\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tloaded.Resources = result.Resources\n\n\tassertion.DebugJSON(\"loaded.Resources\", loaded.Resources)\n\treturn loaded, nil\n}\n\nfunc (l Terraform12ResourceLoader) LoadMany(filenames []string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: []assertion.Resource{},\n\t}\n\tresult, err := loadHCLv2(filenames)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tloaded.Resources = result.Resources\n\n\tassertion.DebugJSON(\"loaded.Resources\", loaded.Resources)\n\treturn loaded, nil\n}\n\nfunc loadHCLv2(paths []string) (Terraform12LoadResult, error) {\n\tresult := Terraform12LoadResult{\n\t\tResources: []assertion.Resource{},\n\t\tData:      []interface{}{},\n\t\tProviders: []interface{}{},\n\t\tModules:   []interface{}{},\n\t\tVariables: []Variable{},\n\t}\n\n\tparser := *tf12parser.New()\n\tblocks, err := parser.ParseMany(paths)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\t// Get all Terraform blocks of a given type and append to the slice of Resources\n\tfor _, blockType := range blockTypes {\n\t\tresult.Resources = append(result.Resources, getBlocksOfType(blocks, blockType)...)\n\t}\n\n\tfor _, resource := range result.Resources {\n\t\tproperties, err := parseJSONDocuments(resource.Properties)\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\t\tresource.Properties = properties\n\t}\n\n\tassertion.Debugf(\"LoadHCL Variables: %v\\n\", result.Variables)\n\treturn result, nil\n}\n\n// Retrieves Terraform blocks of a specific type\n// and places them in a slice of assertion.Resources.\nfunc getBlocksOfType(blocks tf12parser.Blocks, blockCategory string) []assertion.Resource {\n\tvar blockType string\n\tvar blockName string\n\tvar resources []assertion.Resource\n\n\ttfBlocks := blocks.OfType(blockCategory)\n\ti := 0\n\n\tfor _, block := range tfBlocks {\n\n\t\tproperties := attributesToMap(*block)\n\n\t\t// Terraform has labels between 0 and 2 for each block e.g `locals`, `provider`, and `resource`,\n\t\t// and labels could be fixed types or configurable names.\n\t\t// Thus this code checks which label should be used as type and as a name/id. If the block doesn't have\n\t\t// a unique name, then its name/id assigned to an auto-incrementing integer.\n\t\tif assertion.SliceContains(blockLabelSyntax[\"TypeAndName\"], blockCategory) {\n\t\t\tblockType = block.Labels()[0]\n\t\t\tblockName = block.Labels()[1]\n\t\t\tproperties[\"__type__\"] = blockType\n\t\t\tproperties[\"__name__\"] = blockName\n\n\t\t} else if assertion.SliceContains(blockLabelSyntax[\"TypeOnly\"], blockCategory) {\n\t\t\tblockType = block.Labels()[0]\n\t\t\tblockName = strconv.Itoa(i)\n\t\t\ti++\n\t\t\tproperties[\"__type__\"] = blockType\n\t\t\tproperties[\"__name__\"] = blockName\n\n\t\t} else if assertion.SliceContains(blockLabelSyntax[\"NameOnly\"], blockCategory) {\n\t\t\t// A special handling for module to add its source as a type.\n\t\t\tif blockCategory == \"module\" {\n\t\t\t\tblockType = block.GetAttribute(\"source\").Value().AsString()\n\t\t\t} else {\n\t\t\t\tblockType = blockCategory\n\t\t\t}\n\t\t\tblockName = block.Labels()[0]\n\t\t\tproperties[\"__name__\"] = blockName\n\n\t\t} else if assertion.SliceContains(blockLabelSyntax[\"NoTypeNoName\"], blockCategory) {\n\t\t\tblockType = blockCategory\n\t\t\tblockName = strconv.Itoa(i)\n\t\t\ti++\n\t\t}\n\n\t\tresource := assertion.Resource{\n\t\t\tID:         blockName,\n\t\t\tType:       blockType,\n\t\t\tCategory:   blockCategory,\n\t\t\tProperties: properties,\n\t\t\tFilename:   block.Range().Filename,\n\t\t\tLineNumber: block.Range().StartLine,\n\t\t}\n\t\tresources = append(resources, resource)\n\t}\n\treturn resources\n}\n\nfunc attributesToMap(block tf12parser.Block) map[string]interface{} {\n\tpropertyMap := make(map[string]interface{})\n\tallBlocks := block.AllBlocks()\n\tfor _, currentBlock := range allBlocks {\n\t\tvar toAppend []interface{}\n\t\ttoAppend = append(toAppend, attributesToMap(*currentBlock))\n\t\tif propertyMap[currentBlock.Type()] == nil {\n\t\t\tpropertyMap[currentBlock.Type()] = toAppend\n\t\t} else {\n\t\t\tv := propertyMap[currentBlock.Type()].([]interface{})\n\t\t\tv = append(v, toAppend[0])\n\t\t\tpropertyMap[currentBlock.Type()] = v\n\t\t}\n\t}\n\tattributes := block.GetAttributes()\n\tfor _, attribute := range attributes {\n\t\tvalue := attribute.Value()\n\t\tif value.Type().IsTupleType() {\n\t\t\tinnerArray := make([]interface{}, 0)\n\n\t\t\titer := value.ElementIterator()\n\t\t\tfor iter.Next() {\n\t\t\t\t_, iterValue := iter.Element()\n\t\t\t\tif iterValue.CanIterateElements() {\n\t\t\t\t\titerateElements(propertyMap, attribute.Name(), iterValue)\n\t\t\t\t} else {\n\t\t\t\t\tinnerArray = append(innerArray, ctyValueToString(iterValue))\n\t\t\t\t}\n\t\t\t}\n\t\t\tpropertyMap[attribute.Name()] = innerArray\n\t\t} else if value.CanIterateElements() {\n\t\t\titerateElements(propertyMap, attribute.Name(), value)\n\t\t} else {\n\t\t\tsetValue(propertyMap, attribute.Name(), ctyValueToString(value))\n\t\t}\n\t}\n\treturn propertyMap\n}\n\nfunc iterateElements(propertyMap map[string]interface{}, name string, value cty.Value) {\n\tvar innerArray []interface{}\n\tinnerMap := make(map[string]interface{})\n\tinnerArray = append(innerArray, innerMap)\n\tpropertyMap[name] = innerArray\n\n\titer := value.ElementIterator()\n\tfor iter.Next() {\n\t\tkey, value := iter.Element()\n\t\tif value.CanIterateElements() {\n\t\t\titerateElements(innerMap, ctyValueToString(key), value)\n\t\t} else {\n\t\t\tsetValue(innerMap, ctyValueToString(key), ctyValueToString(value))\n\t\t}\n\t}\n}\n\nfunc setValue(m map[string]interface{}, name string, value string) {\n\tenvironmentVariable := getVariableFromEnvironment(name)\n\tif environmentVariable == \"\" {\n\t\tm[name] = value\n\t} else {\n\t\tm[name] = environmentVariable\n\t}\n}\n\nfunc ctyValueToString(value cty.Value) string {\n\t// In case the value is nil but the type is not necessarily <nil>, ~~return an empty string~~\n\t// Update: return an actual string.\n\t// We cannot evaluate tf generated values in tf12, such as referenced arn, but we still want to be able to check for it\n\tif value.IsNull() || !value.IsKnown() {\n\t\treturn \"UNDEFINED\"\n\t} else {\n\t\tswitch value.Type() {\n\t\tcase cty.NilType:\n\t\t\treturn \"\"\n\t\tcase cty.Bool:\n\t\t\tif value.True() {\n\t\t\t\treturn \"true\"\n\t\t\t} else {\n\t\t\t\treturn \"false\"\n\t\t\t}\n\t\tcase cty.String:\n\t\t\treturn strings.Trim(value.AsString(), \"\\n\")\n\t\tcase cty.Number:\n\t\t\tif value.RawEquals(cty.PositiveInfinity) || value.RawEquals(cty.NegativeInfinity) {\n\t\t\t\tpanic(\"cannot convert infinity to string\")\n\t\t\t}\n\t\t\treturn value.AsBigFloat().Text('f', -1)\n\t\tdefault:\n\t\t\tpanic(\"unsupported primitive type\")\n\t\t\t//return \"\"\n\t\t}\n\t}\n}\n\n// PostLoad resolves variable expressions\nfunc (l Terraform12ResourceLoader) PostLoad(inputResources FileResources) ([]assertion.Resource, error) {\n\treturn inputResources.Resources, nil\n}\n"
  },
  {
    "path": "linter/terraform_v12_test.go",
    "content": "package linter\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestTerraformV12Linter(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\tfilenames := []string{\"./testdata/resources/terraform_instance.tf\"}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: Terraform12ResourceLoader{}}\n\truleSet := loadRulesForTest(\"./testdata/rules/terraform_instance.yml\", t)\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, len(report.ResourcesScanned), 1, \"Unexpected number of resources scanned\")\n\tassert.Equal(t, len(report.FilesScanned), 1, \"Unexpected number of files scanned\")\n\tassertViolationsCount(\"TestTerraformLinter \", 0, report.Violations, t)\n}\n\nfunc loadResources12ToTest(t *testing.T, filename string) []assertion.Resource {\n\tloader := Terraform12ResourceLoader{}\n\tloaded, err := loader.Load(filename)\n\tassert.Nil(t, err, \"Expecting Load to run without error\")\n\tresources, err := loader.PostLoad(loaded)\n\tassert.Nil(t, err, \"Expecting PostLoad to run without error\")\n\treturn resources\n}\n\nfunc filterByCategory(t *testing.T, resources []assertion.Resource, categoryName string) []assertion.Resource {\n\tresult := []assertion.Resource{}\n\tfor _, resource := range resources {\n\t\tif resource.Category == categoryName {\n\t\t\tresult = append(result, resource)\n\t\t}\n\t}\n\treturn result\n}\n\nfunc TestSingleResourceType(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, 1, len(resources), \"Expecting 1 resource\")\n\tassert.Equal(t, \"aws_instance\", resources[0].Type)\n\tassert.Equal(t, \"first\", resources[0].ID)\n}\n\nfunc TestResourceDependency(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/tf12_resource_dependency.tf\")\n\tassert.Equal(t, 2, len(resources), \"Expecting 1 resource\")\n}\n\n//The idea of this test is to confirm a particular difference between the original parser and the new\n//I know it's not clear. - MN\nfunc TestTupleType(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/multi_level.tf\")\n\tassert.Equal(t, 1, len(resources), \"Expecting 1 resource\")\n\tstatement := resources[0].Properties.(map[string]interface{})[\"statement\"]\n\tprincipals := statement.([]interface{})[0].(map[string]interface{})[\"principals\"]\n\tidentifiers := principals.([]interface{})[0].(map[string]interface{})[\"identifiers\"]\n\tvalue, ok := identifiers.([]interface{})\n\tassert.True(t, ok)\n\t_, ok = value[0].(string)\n\tassert.True(t, ok)\n}\n\nfunc TestMultipleBlocksOfSameType12(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/multiple_blocks_same.tf\")\n\tassert.Equal(t, 1, len(resources), \"Expecting 1 resource\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tebsBlocks := properties[\"ebs_block_device\"]\n\tassert.Len(t, ebsBlocks, 2)\n}\n\nfunc TestInnerObjects12(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_inner_objects.tf\")\n\tassert.Equal(t, 2, len(resources), \"Expecting 2 resource\")\n\tproperties := resources[1].Properties.(map[string]interface{})\n\tartifactStore := properties[\"artifact_store\"].([]interface{})[0].(map[string]interface{})\n\tencryptionKey := artifactStore[\"encryption_key\"]\n\tassert.NotNil(t, encryptionKey)\n}\n\nfunc TestTerraform12Variable(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, 1, len(resources), \"Expecting 1 resource\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, \"ami-f2d3638a\", properties[\"ami\"], \"Unexpected value for simple variable\")\n}\n\nfunc TestTerraform12VariableWithNoDefault(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, \"UNDEFINED\", tags[\"department\"], \"Unexpected value for variable with no default\")\n}\n\nfunc TestTerraform12FunctionCall(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, \"test\", tags[\"environment\"], \"Unexpected value for lookup function\")\n}\n\nfunc TestTerraform12ListVariable(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\ttags := getResourceTags(resources[0])\n\tassert.Equal(t, tags[\"comment\"], \"bar\", \"Unexpected value for list variable\")\n}\n\nfunc TestTerraform12LocalVariable(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_local_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Expecting 1 resource\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, \"myprojectbucket\", properties[\"name\"], \"Unexpected value for name attribute\")\n}\n\nfunc TestTerraform12VariablesFromEnvironment(t *testing.T) {\n\tos.Setenv(\"TF_VAR_instance_type\", \"c4.large\")\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"instance_type\"], \"c4.large\", \"Unexpected value for instance_type\")\n\tos.Setenv(\"TF_VAR_instance_type\", \"\")\n}\n\nfunc TestTerraform12FileFunction(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/reference_file.tf\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"bucket\"], \"example\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12VariablesInDifferentFile(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\tfilenames := []string{\n\t\t\"./testdata/resources/defines_variables.tf\",\n\t\t\"./testdata/resources/reference_variables.tf\",\n\t}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: Terraform12ResourceLoader{}}\n\truleSet := loadRulesForTest(\"./testdata/rules/terraform_instance.yml\", t)\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, 1, len(report.ResourcesScanned), \"Unexpected number of resources\")\n\tassert.Equal(t, 2, len(report.FilesScanned), \"Unexpected number of files scanned\")\n\tassertViolationsCount(\"TestTerraformVariablesInDifferentFile \", 0, report.Violations, t)\n}\n\nfunc TestTerraform12DataLoader(t *testing.T) {\n\tloader := TerraformResourceLoader{}\n\tloaded, err := loader.Load(\"./testdata/resources/terraform_data.tf\")\n\tassert.Nil(t, err, \"Expecting Load to run without error\")\n\tassert.Equal(t, len(loaded.Resources), 1, \"Unexpected number of resources\")\n}\n\nfunc TestTerraform12ResourceLineNumber(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, 30, resources[0].LineNumber)\n}\n\nfunc TestTerraform12ResourceFileName(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/uses_variables.tf\")\n\tassert.Equal(t, \"./testdata/resources/uses_variables.tf\", resources[0].Filename)\n}\n\nfunc TestTerraform12DataLineNumber(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_data.tf\")\n\tassert.Equal(t, 1, resources[0].LineNumber)\n}\n\nfunc TestTerraform12DataFileName(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_data.tf\")\n\tassert.Equal(t, \"./testdata/resources/terraform_data.tf\", resources[0].Filename)\n}\n\nfunc TestTerraform12ProviderLineNumber(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_provider.tf\")\n\tassert.Equal(t, 1, resources[0].LineNumber)\n}\n\nfunc TestTerraform12ProviderFileName(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_provider.tf\")\n\tassert.Equal(t, \"./testdata/resources/terraform_provider.tf\", resources[0].Filename)\n}\n\nfunc TestTerraform12ModuleLineNumber(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_module.tf\")\n\tassert.Equal(t, 1, resources[0].LineNumber)\n}\n\nfunc TestTerraform12ModuleFileName(t *testing.T) {\n\tresources := loadResources12ToTest(t, \"./testdata/resources/terraform_module.tf\")\n\tassert.Equal(t, \"./testdata/resources/terraform_module.tf\", resources[0].Filename)\n}\n\n// String build message for violations. Debug helper\n//  TODO move to class. remove copy pasted function in tt 12 test\nfunc getViolationsString(violations []assertion.Violation) string {\n\tvar violationsReported string\n\tfor count, v := range violations {\n\t\tviolationsReported += strconv.Itoa(count+1) + \". Violation:\"\n\t\tviolationsReported += \"\\n\\tRule Message: \" + v.RuleMessage\n\t\tviolationsReported += \"\\n\\tRule Id: \" + v.RuleID\n\t\tviolationsReported += \"\\n\\tResource ID: \" + v.ResourceID\n\t\tviolationsReported += \"\\n\\tResource Type: \" + v.ResourceType\n\t\tviolationsReported += \"\\n\\tCategory: \" + v.Category\n\t\tviolationsReported += \"\\n\\tStatus: \" + v.Status\n\t\tviolationsReported += \"\\n\\tAssertion Message: \" + v.AssertionMessage\n\t\tviolationsReported += \"\\n\\tFilename: \" + v.Filename\n\t\tviolationsReported += \"\\n\\tLine Number: \" + strconv.Itoa(v.LineNumber)\n\t\tviolationsReported += \"\\n\\tCreated At: \" + v.CreatedAt + \"\\n\"\n\t}\n\treturn violationsReported\n}\n\nfunc TestTerraform12LinterCases(t *testing.T) {\n\ttestCases := map[string]terraformLinterTestCase{\n\t\t\"ParseError\": {\n\t\t\t\"./testdata/resources/terraform_syntax_error.tf\",\n\t\t\t\"./testdata/rules/terraform_provider.yml\",\n\t\t\t1,\n\t\t\t\"FILE_LOAD\",\n\t\t},\n\t\t\"Provider\": {\n\t\t\t\"./testdata/resources/terraform_provider.tf\",\n\t\t\t\"./testdata/rules/terraform_provider.yml\",\n\t\t\t1,\n\t\t\t\"AWS_PROVIDER\",\n\t\t},\n\t\t\"DataObject\": {\n\t\t\t\"./testdata/resources/terraform_data.tf\",\n\t\t\t\"./testdata/rules/terraform_data.yml\",\n\t\t\t1,\n\t\t\t\"DATA_NOT_CONTAINS\",\n\t\t},\n\t\t\"PoliciesWithVariables\": {\n\t\t\t\"./testdata/resources/policy_with_variables.tf\",\n\t\t\t\"./testdata/rules/policy_variable.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"HereDocWithExpression\": {\n\t\t\t\"./testdata/resources/policy_with_expression.tf\",\n\t\t\t\"./testdata/rules/policy_variable.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"Policies\": {\n\t\t\t\"./testdata/resources/terraform_policy.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t1,\n\t\t\t\"TEST_POLICY\",\n\t\t},\n\t\t\"PolicyInvalidJSON\": {\n\t\t\t\"./testdata/resources/terraform_policy_invalid_json.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"PolicyEmpty\": {\n\t\t\t\"./testdata/resources/terraform_policy_empty.tf\",\n\t\t\t\"./testdata/rules/terraform_policy.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"BatchPrivileged\": {\n\t\t\t\"./testdata/resources/batch_privileged.tf\",\n\t\t\t\"./testdata/rules/batch_definition.yml\",\n\t\t\t1,\n\t\t\t\"BATCH_DEFINITION_PRIVILEGED\",\n\t\t},\n\t\t\"PublicEC2\": {\n\t\t\t\"./testdata/resources/ec2_public.tf\",\n\t\t\t\"./testdata/rules/ec2_public.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"CloudfrontAccessLogs\": {\n\t\t\t\"./testdata/resources/cloudfront_access_logs.tf\",\n\t\t\t\"./testdata/rules/cloudfront_access_logs.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"Module\": {\n\t\t\t\"./testdata/resources/terraform_module.tf\",\n\t\t\t\"./testdata/rules/terraform_module.yml\",\n\t\t\t1,\n\t\t\t\"MODULE_DESCRIPTION\",\n\t\t},\n\t\t\"ElastiCacheRest\": {\n\t\t\t\"./testdata/resources/elasticache_encryption_rest.tf\",\n\t\t\t\"./testdata/rules/elasticache_encryption_rest.yml\",\n\t\t\t1,\n\t\t\t\"ELASTICACHE_ENCRYPTION_REST\",\n\t\t},\n\t\t\"ElastiCacheTransit\": {\n\t\t\t\"./testdata/resources/elasticache_encryption_transit.tf\",\n\t\t\t\"./testdata/rules/elasticache_encryption_transit.yml\",\n\t\t\t1,\n\t\t\t\"ELASTICACHE_ENCRYPTION_TRANSIT\",\n\t\t},\n\t\t\"NeptuneClusterEncryption\": {\n\t\t\t\"./testdata/resources/neptune_db_encryption.tf\",\n\t\t\t\"./testdata/rules/neptune_db_encryption.yml\",\n\t\t\t1,\n\t\t\t\"NEPTUNE_DB_ENCRYPTION\",\n\t\t},\n\t\t\"RdsPublic\": {\n\t\t\t\"./testdata/resources/rds_publicly_available.tf\",\n\t\t\t\"./testdata/rules/rds_publicly_available.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"KinesisKms\": {\n\t\t\t\"./testdata/resources/kinesis_kms_stream.tf\",\n\t\t\t\"./testdata/rules/kinesis_kms_stream.yml\",\n\t\t\t1,\n\t\t\t\"KINESIS_STREAM_KMS\",\n\t\t},\n\t\t\"DmsEncryption\": {\n\t\t\t\"./testdata/resources/dms_endpoint_encryption.tf\",\n\t\t\t\"./testdata/rules/dms_endpoint_encryption.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"EmrClusterLogs\": {\n\t\t\t\"./testdata/resources/emr_cluster_logs.tf\",\n\t\t\t\"./testdata/rules/emr_cluster_logs.yml\",\n\t\t\t1,\n\t\t\t\"AWS_EMR_CLUSTER_LOGGING\",\n\t\t},\n\t\t\"KmsKeyRotation\": {\n\t\t\t\"./testdata/resources/kms_key_rotation.tf\",\n\t\t\t\"./testdata/rules/kms_key_rotation.yml\",\n\t\t\t1,\n\t\t\t\"AWS_KMS_KEY_ROTATION\",\n\t\t},\n\t\t\"SagemakerEndpoint\": {\n\t\t\t\"./testdata/resources/sagemaker_endpoint_encryption.tf\",\n\t\t\t\"./testdata/rules/sagemaker_endpoint_encryption.yml\",\n\t\t\t1,\n\t\t\t\"SAGEMAKER_ENDPOINT_ENCRYPTION\",\n\t\t},\n\t\t\"SagemakerNotebook\": {\n\t\t\t\"./testdata/resources/sagemaker_notebook_encryption.tf\",\n\t\t\t\"./testdata/rules/sagemaker_notebook_encryption.yml\",\n\t\t\t1,\n\t\t\t\"SAGEMAKER_NOTEBOOK_ENCRYPTION\",\n\t\t},\n\t\t\"TF12Variables\": {\n\t\t\t\"./testdata/resources/uses_tf12_variables.tf\",\n\t\t\t\"./testdata/rules/terraform_v12_variables.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"TF12ForLoop\": {\n\t\t\t\"./testdata/resources/tf12_for_loop.tf\",\n\t\t\t\"./testdata/rules/tf12_for_loop.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"TF12NullValue\": {\n\t\t\t\"./testdata/resources/nullable_value.tf\",\n\t\t\t\"./testdata/rules/nullable_value.yml\",\n\t\t\t0,\n\t\t\t\"\",\n\t\t},\n\t\t\"TF12DynamicBlock\": {\n\t\t\t\"./testdata/resources/dynamic_block.tf\",\n\t\t\t\"./testdata/rules/dynamic_block.yml\",\n\t\t\t1,\n\t\t\t\"NO_SSH_ACCESS\",\n\t\t},\n\t\t\"TF12Tagging\": {\n\t\t\t\"./testdata/resources/tagging.tf\",\n\t\t\t\"./testdata/rules/tagging.yml\",\n\t\t\t5,\n\t\t\t\"TAG_VALID\",\n\t\t},\n\t\t\"TF12ExplicitChar\": {\n\t\t\t\"./testdata/resources/explicit_chars.tf\",\n\t\t\t\"./testdata/rules/explicit_chars.yml\",\n\t\t\t1,\n\t\t\t\"CHECK_FOR_COLON\",\n\t\t},\n\t}\n\n\tfor name, tc := range testCases {\n\t\toptions := Options{\n\t\t\tTags:    []string{},\n\t\t\tRuleIDs: []string{},\n\t\t}\n\t\tfilenames := []string{tc.ConfigurationFilename}\n\t\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: Terraform12ResourceLoader{}}\n\t\truleSet := loadRulesForTest(tc.RulesFilename, t)\n\t\treport, err := linter.Validate(ruleSet, options)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Expecting %s to return without an error: %s\", name, err.Error())\n\t\t}\n\t\tif len(report.FilesScanned) != 1 {\n\t\t\tt.Errorf(\"TestTerraformLinterCases scanned %d files, expecting 1\", len(report.FilesScanned))\n\t\t}\n\t\tif len(report.Violations) != tc.ExpectedViolationCount {\n\t\t\tt.Errorf(\"%s returned %d violations, expecting %d\", name, len(report.Violations), tc.ExpectedViolationCount)\n\t\t\tviolationsReported := getViolationsString(report.Violations)\n\t\t\tt.Errorf(\"\\nViolations: %v\", violationsReported)\n\t\t}\n\t\tif tc.ExpectedViolationRuleID != \"\" {\n\t\t\tassertViolationByRuleID(name, tc.ExpectedViolationRuleID, report.Violations, t)\n\t\t}\n\t}\n}\n\nfunc TestTerraform12FileFunctionMultiLineContent(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/reference_file_multi_line.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 2, \"Unexpected number of resources found\")\n\tproperties_1 := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties_1[\"test_value\"], \"multi\\nline\\nexample\", \"Unexpected value for bucket property\")\n\tproperties_2 := resources[1].Properties.(map[string]interface{})\n\tassert.Equal(t, properties_2[\"test_value2\"], properties_1[\"test_value\"], \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12FileFunctionResourceFileAbsolutePath(t *testing.T) {\n\tabsolutePath, _ := filepath.Abs(\"./testdata/resources/reference_file.tf\")\n\tresources := loadResources12ToTest(t, absolutePath)\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"bucket\"], \"example\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12FileFunctionTemplateFileFunction(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/template_file_function_basic.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"bucket\"], \"bucket-foo-example-bar\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12FileFunctionTemplateFileForLoop(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/template_file_function_for_loop.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"test_value\"], \"testing:foo\\ntesting:bar\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12FileFunctionTemplateFileConditional(t *testing.T) {\n\tallResources := loadResources12ToTest(t, \"./testdata/resources/template_file_function_conditional.tf\")\n\tresources := filterByCategory(t, allResources, \"resource\")\n\tassert.Equal(t, len(resources), 2, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"test_value\"], \"Foo\", \"Unexpected value for bucket property\")\n\tproperties2 := resources[1].Properties.(map[string]interface{})\n\tassert.Equal(t, properties2[\"test_value2\"], \"Bar\", \"Unexpected value for bucket property\")\n}\n\nfunc TestTerraform12FileFunctionReferenceFileAbsoultePath(t *testing.T) {\n\tpath, _ := os.Getwd()\n\tvar err error\n\tvar tempResourceFile *os.File\n\tvar tempReferenceFile *os.File\n\tvar tempResourceDir string\n\tvar tempReferenceDir string\n\n\ttempResourceDir, err = ioutil.TempDir(path, \"tf_resource\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\ttempReferenceDir, err = ioutil.TempDir(tempResourceDir, \"tf_reference\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\ttempResourceFile, err = ioutil.TempFile(tempResourceDir, \"test_resource.tf\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\ttempReferenceFile, err = ioutil.TempFile(tempReferenceDir, \"test_reference.txt\")\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\t// tempReferenceFile.Name() is returned as the Absolute Path of the temp reference file\n\ttf12ResourceContent := fmt.Sprintf(`resource \"aws_s3_bucket\" \"a_bucket\" {\n bucket = \"${file(\"%v\")}\"\n}\n`, tempReferenceFile.Name())\n\n\ttf12ReferenceContent := (`example\n`)\n\terr = ioutil.WriteFile(tempResourceFile.Name(), []byte(tf12ResourceContent), 0644)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\terr = ioutil.WriteFile(tempReferenceFile.Name(), []byte(tf12ReferenceContent), 0644)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tresources := loadResources12ToTest(t, tempResourceFile.Name())\n\tassert.Equal(t, len(resources), 1, \"Unexpected number of resources found\")\n\tproperties := resources[0].Properties.(map[string]interface{})\n\tassert.Equal(t, properties[\"bucket\"], \"example\", \"Unexpected value for bucket property\")\n\tos.RemoveAll(tempResourceDir)\n\tos.RemoveAll(tempReferenceDir)\n}\n"
  },
  {
    "path": "linter/testdata/data/bucket_name",
    "content": "example\n"
  },
  {
    "path": "linter/testdata/data/multi_line_content",
    "content": "multi\nline\nexample\n"
  },
  {
    "path": "linter/testdata/data/reference_relative.tf",
    "content": "resource \"aws_s3_bucket\" \"a_bucket\" {\n  bucket = \"${file(\"bucket_name\")}\"\n}\n"
  },
  {
    "path": "linter/testdata/data/template_file_example_basic",
    "content": "bucket-${var1}-example-${var2}\n"
  },
  {
    "path": "linter/testdata/data/template_file_example_conditional",
    "content": "%{ if test_var == \"Alpha\" }\nFoo\n%{ else }\nBar\n%{ endif ~}\n"
  },
  {
    "path": "linter/testdata/data/template_file_example_for_loop",
    "content": "%{ for word in words ~}\ntesting:${word}\n%{ endfor ~}\n"
  },
  {
    "path": "linter/testdata/resources/batch_privileged.tf",
    "content": "resource \"aws_batch_job_definition\" \"test\" {\n    name = \"tf_test_batch_job_definition\"\n    type = \"container\"\n    container_properties = <<EOF\n{\n    \"privileged\": true,\n    \"command\": [\"ls\", \"-la\"],\n    \"image\": \"busybox\",\n    \"memory\": 1024,\n    \"vcpus\": 1,\n    \"volumes\": [\n      {\n        \"host\": {\n          \"sourcePath\": \"/tmp\"\n        },\n        \"name\": \"tmp\"\n      }\n    ],\n    \"environment\": [\n        {\"name\": \"VARNAME\", \"value\": \"VARVAL\"}\n    ],\n    \"mountPoints\": [\n        {\n          \"sourceVolume\": \"tmp\",\n          \"containerPath\": \"/tmp\",\n          \"readOnly\": false\n        }\n    ],\n    \"ulimits\": [\n      {\n        \"hardLimit\": 1024,\n        \"name\": \"nofile\",\n        \"softLimit\": 1024\n      }\n    ]\n}\nEOF\n}\n\n"
  },
  {
    "path": "linter/testdata/resources/cloudfront_access_logs.tf",
    "content": "resource \"aws_cloudfront_distribution\" \"s3_distribution\" {\n  origin {\n    domain_name = \"${aws_s3_bucket.b.bucket_regional_domain_name}\"\n    origin_id   = \"${local.s3_origin_id}\"\n\n    s3_origin_config {\n      origin_access_identity = \"origin-access-identity/cloudfront/ABCDEFG1234567\"\n    }\n  }\n\n  enabled             = true\n  is_ipv6_enabled     = true\n  comment             = \"Some comment\"\n  default_root_object = \"index.html\"\n\n  logging_config {\n    include_cookies = false\n    bucket          = \"mylogs.s3.amazonaws.com\"\n    prefix          = \"myprefix\"\n  }\n\n  aliases = [\"mysite.example.com\", \"yoursite.example.com\"]\n\n  default_cache_behavior {\n    allowed_methods  = [\"DELETE\", \"GET\", \"HEAD\", \"OPTIONS\", \"PATCH\", \"POST\", \"PUT\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"${local.s3_origin_id}\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    viewer_protocol_policy = \"allow-all\"\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n  }\n\n  # Cache behavior with precedence 0\n  ordered_cache_behavior {\n    path_pattern     = \"/content/immutable/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    target_origin_id = \"${local.s3_origin_id}\"\n\n    forwarded_values {\n      query_string = false\n      headers      = [\"Origin\"]\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 86400\n    max_ttl                = 31536000\n    compress               = true\n    viewer_protocol_policy = \"redirect-to-https\"\n  }\n\n  # Cache behavior with precedence 1\n  ordered_cache_behavior {\n    path_pattern     = \"/content/*\"\n    allowed_methods  = [\"GET\", \"HEAD\", \"OPTIONS\"]\n    cached_methods   = [\"GET\", \"HEAD\"]\n    target_origin_id = \"${local.s3_origin_id}\"\n\n    forwarded_values {\n      query_string = false\n\n      cookies {\n        forward = \"none\"\n      }\n    }\n\n    min_ttl                = 0\n    default_ttl            = 3600\n    max_ttl                = 86400\n    compress               = true\n    viewer_protocol_policy = \"redirect-to-https\"\n  }\n\n  price_class = \"PriceClass_200\"\n\n  restrictions {\n    geo_restriction {\n      restriction_type = \"whitelist\"\n      locations        = [\"US\", \"CA\", \"GB\", \"DE\"]\n    }\n  }\n\n  tags = {\n    Environment = \"production\"\n  }\n\n  viewer_certificate {\n    cloudfront_default_certificate = true\n  }\n}"
  },
  {
    "path": "linter/testdata/resources/defines_variables.tf",
    "content": "variable \"instance_type\" {\n  default = \"t2.micro\"\n}\n\nvariable \"ami\" {\n  default = \"ami-f2d3638a\"\n}\n\nvariable \"project\" {\n  default = \"demo\"\n}\n\n"
  },
  {
    "path": "linter/testdata/resources/dms_endpoint_encryption.tf",
    "content": "resource \"aws_dms_endpoint\" \"test\" {\n  certificate_arn             = \"arn:aws:acm:us-east-1:123456789012:certificate/12345678-1234-1234-1234-123456789012\"\n  database_name               = \"test\"\n  endpoint_id                 = \"test-dms-endpoint-tf\"\n  endpoint_type               = \"source\"\n  engine_name                 = \"aurora\"\n  extra_connection_attributes = \"\"\n  kms_key_arn                 = \"arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012\"\n  password                    = \"test\"\n  port                        = 3306\n  server_name                 = \"test\"\n  ssl_mode                    = \"none\"\n\n  tags = {\n    Name = \"test\"\n  }\n\n  username = \"test\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/dynamic_block.tf",
    "content": "terraform {\n  required_version = \">= 0.12.0\"\n}\n\nvariable \"service_ports\" {\n  default = [22, 80, 1433, 6379]\n}\n\nresource \"aws_security_group\" \"example\" {\n  name = \"example\"\n\n  dynamic \"ingress\" {\n    for_each = var.service_ports\n    content {\n      from_port = ingress.value\n      to_port   = ingress.value\n      protocol  = \"tcp\"\n    }\n  }\n\n  egress {\n    from_port = 443\n    to_port = 443\n    protocol = \"tcp\"\n  }\n}"
  },
  {
    "path": "linter/testdata/resources/ec2_public.tf",
    "content": "resource \"aws_subnet\" \"main\" {\n  vpc_id     = \"${aws_vpc.main.id}\"\n  cidr_block = \"10.0.1.0/24\"\n\n  tags = {\n    Name = \"Main\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/elasticache_encryption_rest.tf",
    "content": "resource \"aws_elasticache_replication_group\" \"example\" {\n  automatic_failover_enabled    = true\n  availability_zones            = [\"us-west-2a\", \"us-west-2b\"]\n  replication_group_id          = \"tf-rep-group-1\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  parameter_group_name          = \"default.redis3.2\"\n  port                          = 6379\n\n  lifecycle {\n    ignore_changes = [\"number_cache_clusters\"]\n  }\n}\n\nresource \"aws_elasticache_cluster\" \"replica\" {\n  count = 1\n\n  cluster_id           = \"tf-rep-group-1-${count.index}\"\n  replication_group_id = \"${aws_elasticache_replication_group.example.id}\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/elasticache_encryption_transit.tf",
    "content": "resource \"aws_elasticache_replication_group\" \"example\" {\n  automatic_failover_enabled    = true\n  availability_zones            = [\"us-west-2a\", \"us-west-2b\"]\n  replication_group_id          = \"tf-rep-group-1\"\n  replication_group_description = \"test description\"\n  node_type                     = \"cache.m4.large\"\n  number_cache_clusters         = 2\n  parameter_group_name          = \"default.redis3.2\"\n  port                          = 6379\n\n  lifecycle {\n    ignore_changes = [\"number_cache_clusters\"]\n  }\n}\n\nresource \"aws_elasticache_cluster\" \"replica\" {\n  count = 1\n\n  cluster_id           = \"tf-rep-group-1-${count.index}\"\n  replication_group_id = \"${aws_elasticache_replication_group.example.id}\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/embedded_yaml.yml",
    "content": "---\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: config\ndata:\n  config1.yml: |-\n    ---\n    # this is an embedded YAML file\n    foo:\n      bar:\n        key1: value\n        key2: value\n          unit_count: 1\n  config2.yml: |-\n    ---\n    # another embedded YAML file\n    logging:\n      loglevel: INFO\n      logformat: default\n---\napiVersion: 1\nkind: ConfigMap\nmetadata:\n  name: config2\ndata:\n  message: Hello\n"
  },
  {
    "path": "linter/testdata/resources/empty_document.yml",
    "content": "---\n#apiVersion: v1\n#kind: Pod\n#metadata:\n#  name: web1\n#  namespace: web\n#spec:\n#  containers:\n#  - name: nginx\n#    image: nginx:1.7.9\n#    ports:\n#    - containerPort: 80\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: web2\n  namespace: web\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n---\n\n"
  },
  {
    "path": "linter/testdata/resources/emr_cluster_logs.tf",
    "content": "resource \"aws_emr_cluster\" \"cluster\" {\n  name          = \"emr-test-arn\"\n  release_label = \"emr-4.6.0\"\n  applications  = [\"Spark\"]\n\n  additional_info = <<EOF\n{\n  \"instanceAwsClientConfiguration\": {\n    \"proxyPort\": 8099,\n    \"proxyHost\": \"myproxy.example.com\"\n  }\n}\nEOF\n\n  termination_protection            = false\n  keep_job_flow_alive_when_no_steps = true\n\n  ec2_attributes {\n    subnet_id                         = \"${aws_subnet.main.id}\"\n    emr_managed_master_security_group = \"${aws_security_group.sg.id}\"\n    emr_managed_slave_security_group  = \"${aws_security_group.sg.id}\"\n    instance_profile                  = \"${aws_iam_instance_profile.emr_profile.arn}\"\n  }\n\n  master_instance_group {\n    instance_type = \"m4.large\"\n  }\n\n  core_instance_group {\n    instance_type  = \"c4.large\"\n    instance_count = 1\n\n    ebs_config {\n      size                 = \"40\"\n      type                 = \"gp2\"\n      volumes_per_instance = 1\n    }\n\n    bid_price = \"0.30\"\n\n    autoscaling_policy = <<EOF\n{\n\"Constraints\": {\n  \"MinCapacity\": 1,\n  \"MaxCapacity\": 2\n},\n\"Rules\": [\n  {\n    \"Name\": \"ScaleOutMemoryPercentage\",\n    \"Description\": \"Scale out if YARNMemoryAvailablePercentage is less than 15\",\n    \"Action\": {\n      \"SimpleScalingPolicyConfiguration\": {\n        \"AdjustmentType\": \"CHANGE_IN_CAPACITY\",\n        \"ScalingAdjustment\": 1,\n        \"CoolDown\": 300\n      }\n    },\n    \"Trigger\": {\n      \"CloudWatchAlarmDefinition\": {\n        \"ComparisonOperator\": \"LESS_THAN\",\n        \"EvaluationPeriods\": 1,\n        \"MetricName\": \"YARNMemoryAvailablePercentage\",\n        \"Namespace\": \"AWS/ElasticMapReduce\",\n        \"Period\": 300,\n        \"Statistic\": \"AVERAGE\",\n        \"Threshold\": 15.0,\n        \"Unit\": \"PERCENT\"\n      }\n    }\n  }\n]\n}\nEOF\n  }\n\n  ebs_root_volume_size = 100\n\n  tags = {\n    role = \"rolename\"\n    env  = \"env\"\n  }\n\n  bootstrap_action {\n    path = \"s3://elasticmapreduce/bootstrap-actions/run-if\"\n    name = \"runif\"\n    args = [\"instance.isMaster=true\", \"echo running on master node\"]\n  }\n\n  configurations_json = <<EOF\n  [\n    {\n      \"Classification\": \"hadoop-env\",\n      \"Configurations\": [\n        {\n          \"Classification\": \"export\",\n          \"Properties\": {\n            \"JAVA_HOME\": \"/usr/lib/jvm/java-1.8.0\"\n          }\n        }\n      ],\n      \"Properties\": {}\n    },\n    {\n      \"Classification\": \"spark-env\",\n      \"Configurations\": [\n        {\n          \"Classification\": \"export\",\n          \"Properties\": {\n            \"JAVA_HOME\": \"/usr/lib/jvm/java-1.8.0\"\n          }\n        }\n      ],\n      \"Properties\": {}\n    }\n  ]\nEOF\n\n  service_role = \"${aws_iam_role.iam_emr_service_role.arn}\"\n}\n\n\n"
  },
  {
    "path": "linter/testdata/resources/explicit_chars.tf",
    "content": "# Fail\nresource \"aws_s3_bucket_policy\" \"a\" {\n  bucket = aws_s3_bucket.a.id\n  policy =<<POLICY\n{\n    \"Version\": \"2018-08-09\",\n    \"Statement\": [\n    {\n        \"Effect\": \"Deny\",\n        \"Action\": \"s3:*\",\n        \"Principal\": {\"AWS\": [\n            \"*\"\n        ]},\n        \"Resource\": [\n            \"arn:aws:s3:::BUCKETNAME\",\n            \"arn:aws:s3:::BUCKETNAME/*\"\n        ],\n        \"Condition\": { \"Bool\": { \"aws:SecureTransport\": \"true\" } }\n    }]\n}\nPOLICY\n}\n\n# Pass\nresource \"aws_s3_bucket_policy\" \"b\" {\n  bucket = aws_s3_bucket.b.id\n  policy =<<POLICY\n{\n    \"Version\": \"2018-08-09\",\n    \"Statement\": [\n    {\n        \"Effect\": \"Deny\",\n        \"Action\": \"s3:*\",\n        \"Principal\": {\"AWS\": [\n            \"*\"\n        ]},\n        \"Resource\": [\n            \"arn:aws:s3:::BUCKETNAME\",\n            \"arn:aws:s3:::BUCKETNAME/*\"\n        ],\n        \"Condition\": { \"Bool\": { \"aws:SecureTransport\": \"false\" } }\n    }]\n}\nPOLICY\n}\n"
  },
  {
    "path": "linter/testdata/resources/generic.config",
    "content": "#\nwidgets:\n  - id: W1\n    name: Foo\n  - id: W2\n    name: Bar\n  - id: W3\n    key: Baz\n\ngadgets:\n  - name: first_gadget\n    color: red\n  - name: second_gadget\n    color: blue\n  - name: third_gadget\n    color: green\n  - name: fourth_gadget\n    color: yellow\n\nother_stuff:\n  contraptions:\n    - ids:\n        serial_number: S1000\n        sku: S1234\n      size: 10\n      locations:\n        - city: Seattle\n        - city: San Francisco\n    - ids:\n        serial_number: S2000\n        sku: S5678\n      size: 20\n      locations:\n        - city: New York\n    - ids:\n        serial_number: S3000\n        sku: S0101\n      size: 4000\n      locations:\n        - city: Paris\n        - city: Munich\n        - city: Florence\n"
  },
  {
    "path": "linter/testdata/resources/invalid.yml",
    "content": "version:1\ndescription:File with invalid YAML\ntype:Terraform\nrules:\n  - id:TEST_POLICY\n   message:Testing\n"
  },
  {
    "path": "linter/testdata/resources/kinesis_kms_stream.tf",
    "content": "resource \"aws_kinesis_stream\" \"test_stream\" {\n  name             = \"terraform-kinesis-test\"\n  shard_count      = 1\n  retention_period = 48\n\n  shard_level_metrics = [\n    \"IncomingBytes\",\n    \"OutgoingBytes\",\n  ]\n\n  tags = {\n    Environment = \"test\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/kms_key_rotation.tf",
    "content": "resource \"aws_kms_key\" \"a\" {\n  description             = \"KMS key 1\"\n  deletion_window_in_days = 10\n}"
  },
  {
    "path": "linter/testdata/resources/missing_kind.yml",
    "content": "---\napiVersion: v1\n#kind: Pod\nmetadata:\n  name: web1\n  namespace: web\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n"
  },
  {
    "path": "linter/testdata/resources/multi_level.tf",
    "content": "data \"aws_iam_policy_document\" \"list_principal_identifier\" {\n  policy_id = \"__default_policy_ID\"\n\n  statement {\n    actions = [\n      \"SNS:Subscribe\",\n      \"SNS:SetTopicAttributes\",\n      \"SNS:RemovePermission\",\n      \"SNS:Receive\",\n      \"SNS:Publish\",\n      \"SNS:ListSubscriptionsByTopic\",\n      \"SNS:GetTopicAttributes\",\n      \"SNS:DeleteTopic\",\n      \"SNS:AddPermission\",\n    ]\n\n    effect = \"Allow\"\n\n    principals {\n      type        = \"AWS\"\n      identifiers = [\"*\"]\n    }\n\n    resources = [\n      \"${aws_sns_topic.test.arn}\",\n    ]\n\n    sid = \"__default_statement_ID\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/multiple_blocks_same.tf",
    "content": "resource \"aws_instance\" \"foo\" {\n  ebs_block_device {\n    encrypted   = false\n  }\n\n  ebs_block_device {\n    encrypted   = true\n  }\n\n  credit_specification {\n    cpu_credits = \"unlimited\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/multiple_pods.yml",
    "content": "---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: web1\n  namespace: web\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n---\napiVersion: v1\nkind: Pod\nmetadata:\n  name: web2\n  namespace: web\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n---\n"
  },
  {
    "path": "linter/testdata/resources/neptune_db_encryption.tf",
    "content": "resource \"aws_neptune_cluster\" \"default\" {\n  cluster_identifier                  = \"neptune-cluster-demo\"\n  engine                              = \"neptune\"\n  backup_retention_period             = 5\n  preferred_backup_window             = \"07:00-09:00\"\n  skip_final_snapshot                 = true\n  iam_database_authentication_enabled = true\n  apply_immediately                   = true\n}\n"
  },
  {
    "path": "linter/testdata/resources/nullable_value.tf",
    "content": "variable \"is_jump_host\" {\n  default = false\n}\n\nresource \"aws_instance\" \"nullable\" {\n  ami = \"ami-f2d3638a\"\n  instance_type = \"t2.micro\"\n  key_name = var.is_jump_host ? \"my_aws_key\" : null\n}"
  },
  {
    "path": "linter/testdata/resources/pod.yml",
    "content": "apiVersion: v1\nkind: Pod\nmetadata:\n  name: nginx\n  namespace: web\nspec:\n  containers:\n  - name: nginx\n    image: nginx:1.7.9\n    ports:\n    - containerPort: 80\n"
  },
  {
    "path": "linter/testdata/resources/policy_with_expression.tf",
    "content": "variable \"resources\" {\n    default = \"None\"\n}\n\nresource \"aws_iam_role\" \"role_with_variable\" {\n    name = \"non_compliant\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n     {\n        \"Action\": \"*\",\n        \"Principal\": { \"Service\": \"ec2.amazonaws.com\" },\n        \"Effect\": \"Allow\", \n        \"Resources\": \"${var.resources == \"foo\" ? \"foo\": \"*\"}\"\n     }\n  ]\n}\nEOF\n}\n\n"
  },
  {
    "path": "linter/testdata/resources/policy_with_variables.tf",
    "content": "variable \"statement_effect\" {\n  default = \"Allow\"\n}\n\nresource \"aws_iam_role\" \"role_with_variable\" {\n    name = \"non_compliant\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n     {\n        \"Action\": \"*\",\n        \"Principal\": { \"Service\": \"ec2.amazonaws.com\" },\n        \"Effect\": \"${var.statement_effect}\",\n        \"Resources\": \"*\"\n     }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "linter/testdata/resources/rds_publicly_available.tf",
    "content": "resource \"aws_db_instance\" \"default\" {\n  allocated_storage    = 20\n  storage_type         = \"gp2\"\n  engine               = \"mysql\"\n  engine_version       = \"5.7\"\n  instance_class       = \"db.t2.micro\"\n  name                 = \"mydb\"\n  username             = \"foo\"\n  password             = \"foobarbaz\"\n  parameter_group_name = \"default.mysql5.7\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/reference_file.tf",
    "content": "resource \"aws_s3_bucket\" \"a_bucket\" {\n  bucket = \"${file(\"./testdata/data/bucket_name\")}\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/reference_file_multi_line.tf",
    "content": "terraform {\n  required_version = \">= 0.12.0\"\n}\n\nresource \"test_resource\" \"test\" {\n  test_value = file(\"./testdata/data/multi_line_content\")\n}\n\nresource \"test_resource2\" \"test2\" {\n  test_value2 = <<EOT\nmulti\nline\nexample\nEOT\n}\n"
  },
  {
    "path": "linter/testdata/resources/reference_variables.tf",
    "content": "resource \"aws_instance\" \"first\" {\n  ami = \"${var.ami}\"\n  instance_type = \"${var.instance_type}\"\n  tags = {\n    project = \"${var.project}\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/sagemaker_endpoint_encryption.tf",
    "content": "\nresource \"aws_sagemaker_endpoint_configuration\" \"ec\" {\nname = \"my-endpoint-config\"\n\nproduction_variants {\nvariant_name           = \"variant-1\"\nmodel_name             = \"${aws_sagemaker_model.m.name}\"\ninitial_instance_count = 1\ninstance_type          = \"ml.t2.medium\"\n}\n\ntags {\nName = \"foo\"\n}\n}"
  },
  {
    "path": "linter/testdata/resources/sagemaker_notebook_encryption.tf",
    "content": "resource \"aws_sagemaker_notebook_instance\" \"ni\" {\n  name          = \"my-notebook-instance\"\n  role_arn      = \"${aws_iam_role.role.arn}\"\n  instance_type = \"ml.t2.medium\"\n\n  tags = {\n    Name = \"foo\"\n  }\n}"
  },
  {
    "path": "linter/testdata/resources/tagging.tf",
    "content": "locals {\n  TAGS_WITH_DATA_CLASS = {\n    managed_by       = \"Terraform Process\"\n    data_class       = \"internal\"\n  }\n  TAGS_WITH_INVALID_DATA_CLASS = {\n    managed_by       = \"Terraform Process\"\n    data_class       = \"invalid\"\n  }\n  TAGS_WITHOUT_DATA_CLASS = {\n    managed_by       = \"Terraform Process\"\n  }\n}\n\n# Pass\nresource \"aws_db_instance\" \"pass_main_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = {\"data_class\" = \"internal\"}\n\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"fail_main_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = {\"data_class\" = \"somethingelse\"}\n\n  storage_encrypted = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"pass_with_merge_main_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITHOUT_DATA_CLASS,\n    {\"data_class\" = \"internal\"}\n  )\n\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"missing_tags_main_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"missing_data_class_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITHOUT_DATA_CLASS,\n    {\"somethingelse\" = \"example\"}\n  )\n  storage_encrypted = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"inherit_data_class_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITH_DATA_CLASS,\n    {\"somethingelse\" = \"example\"}\n  )\n  storage_encrypted = true\n}\n\n# Fail\nresource \"aws_db_instance\" \"inherit_invalid_data_class_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITH_INVALID_DATA_CLASS,\n    {\"somethingelse\" = \"example\"}\n  )\n  storage_encrypted = true\n}\n\n\n# Fail\nresource \"aws_db_instance\" \"invalid_data_class_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITHOUT_DATA_CLASS,\n    {\"data_class\" = \"somethingelse\"}\n  )\n  storage_encrypted = true\n}\n\n# Pass\nresource \"aws_db_instance\" \"inherit_data_class_db\" {\n  count                     = 1\n  allocated_storage         = 100\n  max_allocated_storage     = 150\n  storage_type              = \"gp2\"\n  engine                    = \"mysql\"\n  engine_version            = \"5.7\"\n  instance_class            = \"db.t2.micro\"\n  name                      = \"test-data_class-tag\"\n  username                  = \"testUser\"\n  password                  = \"temppw-test1234%\"\n  tags = merge(\n    local.TAGS_WITH_DATA_CLASS,\n    {\"somethingelse\" = \"example\"}\n  )\n  storage_encrypted = true\n}\n"
  },
  {
    "path": "linter/testdata/resources/template_file_function_basic.tf",
    "content": "terraform {\n  required_version = \">= 0.12.0\"\n}\n\nvariable \"object_1\" {\n  default = \"foo\"\n}\n\nvariable \"object_2\" {\n  default = \"bar\"\n}\n\nresource \"aws_s3_bucket\" \"a_bucket\" {\n  bucket = templatefile(\"./testdata/data/template_file_example_basic\", { var1 = var.object_1, var2 = var.object_2})\n}"
  },
  {
    "path": "linter/testdata/resources/template_file_function_conditional.tf",
    "content": "terraform {\n  required_version = \">= 0.12.0\"\n}\n\nvariable \"var1\" {\n  default = \"Alpha\"\n}\n\nvariable \"var2\" {\n  default = \"Bravo\"\n}\n\nresource \"test_resource\" \"test\" {\n  test_value =  templatefile(\"./testdata/data/template_file_example_conditional\", { test_var = var.var1})\n}\n\nresource \"test_resource\" \"test2\" {\n  test_value2 =  templatefile(\"./testdata/data/template_file_example_conditional\", { test_var = var.var2})\n}"
  },
  {
    "path": "linter/testdata/resources/template_file_function_for_loop.tf",
    "content": "terraform {\n  required_version = \">= 0.12.0\"\n}\n\nvariable \"words\" {\n  default = [\"foo\", \"bar\"]\n}\n\nresource \"test\" \"test_resource\" {\n  test_value = templatefile(\"./testdata/data/template_file_example_for_loop\", { words = var.words})\n}"
  },
  {
    "path": "linter/testdata/resources/terraform_data.tf",
    "content": "data \"template_file\" \"example\" {\n    template = \"/example/template.json\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/terraform_inner_objects.tf",
    "content": "data \"aws_kms_alias\" \"s3kmskey\" {\n  name = \"alias/myKmsKey\"\n}\n\nresource \"aws_codepipeline\" \"foo\" {\n  artifact_store = {\n    encryption_key = {\n      id   = \"id\"\n      type = \"KMS\"\n    }\n  }\n}"
  },
  {
    "path": "linter/testdata/resources/terraform_instance.tf",
    "content": "resource \"aws_instance\" \"first\" {\n    ami = \"ami-f2d3638a\"\n    instance_type = \"t2.micro\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/terraform_module.tf",
    "content": "module \"test_module\" {\n   source = \"./modules/foo\"\n   name = \"counter_1\"\n   count = 4\n}\n\nmodule \"test_module_with_description\" {\n   source = \"./modules/foo\"\n   name = \"counter_2\"\n   description = \"here is a description\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/terraform_policy.tf",
    "content": "resource \"aws_iam_role\" \"role1\" {\n    name = \"role1\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Action\": \"sts:AssumeRole\",\n      \"Principal\": {\n        \"Service\": \"ec2.amazonaws.com\"\n      },\n      \"Effect\": \"Allow\",\n      \"Sid\": \"\"\n    }\n  ]\n}\nEOF\n}\n"
  },
  {
    "path": "linter/testdata/resources/terraform_policy_empty.tf",
    "content": "resource \"aws_iam_role\" \"role1\" {\n    name = \"role1\"\n    assume_role_policy = <<EOF\nEOF\n}\n"
  },
  {
    "path": "linter/testdata/resources/terraform_policy_invalid_json.tf",
    "content": "resource \"aws_iam_role\" \"role1\" {\n    name = \"role1\"\n    assume_role_policy = <<EOF\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": }\n}\nEOF\n}\n\n"
  },
  {
    "path": "linter/testdata/resources/terraform_provider.tf",
    "content": "provider \"aws\" {\n   access_key = \"AKIAIOSFODNN7EXAMPLE\"\n   secret_key = \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\"\n}\n\nprovider \"safe\" {\n} \n"
  },
  {
    "path": "linter/testdata/resources/terraform_syntax_error.tf",
    "content": "data \"template_file\" \"example\" }\n    template = \"/example/template.json\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/tf12_for_loop.tf",
    "content": "# Configuration for Terraform 0.12\n\nvariable \"vpc_id\" {\n  description = \"ID for the AWS VPC where a security group is to be created.\"\n}\n\nvariable \"subnet_numbers\" {\n  description = \"List of 8-bit numbers of subnets of base_cidr_block that should be granted access.\"\n default = [1, 2, 3]\n}\n\ndata \"aws_vpc\" \"example\" {\n  id = var.vpc_id\n  vpc_cidr = \"10.1.0.0/16\"\n}\n\nresource \"aws_security_group\" \"example\" {\n  name        = \"friendly_subnets\"\n  description = \"Allows access from friendly subnets\"\n  vpc_id      = var.vpc_id\n\n  ingress {\n    from_port = 0\n    to_port   = 0\n    protocol  = -1\n\n    # For each number in subnet_numbers, extend the CIDR prefix of the\n    # requested VPC to produce a subnet CIDR prefix.\n    # For the default value of subnet_numbers above and a VPC CIDR prefix\n    # of 10.1.0.0/16, this would produce:\n    #   [\"10.1.1.0/24\", \"10.1.2.0/24\", \"10.1.3.0/24\"]\n    cidr_blocks = [\n    for num in var.subnet_numbers:\n    cidrsubnet(data.aws_vpc.example.vpc_cidr, 8, num)\n//    cidrsubnet(data.aws_vpc.example.cidr_block, 8, num)\n    ]\n  }\n}"
  },
  {
    "path": "linter/testdata/resources/tf12_resource_dependency.tf",
    "content": "resource \"null_resource\" \"valid_engine_version_check\" {\n  count = 1\n  #\"ERROR: elasticache_version_options can only be: 3.2.6, 4.0.10 or 5.0.5, Recommended version is 5.0.5 as that is the current GA release of Redis, see: https://docs.aws.amazon.com/AmazonElastiCache/latest/red-ug/supported-engine-versions.html\" = true\n}\n\nresource \"aws_elasticache_replication_group\" \"redis\" {\n  depends_on                    = [null_resource.valid_engine_version_check]\n}\n"
  },
  {
    "path": "linter/testdata/resources/users.csv",
    "content": "admin,Admin\nreadonly,Audit\nuser1,\n"
  },
  {
    "path": "linter/testdata/resources/users.json",
    "content": "{\n    \"Users\": [\n        {\n            \"UserName\": \"admin\",\n            \"Department\": \"Cloud\"\n        }, \n        {\n            \"UserName\": \"readonly\",\n            \"Department\": \"Audit\"\n        }, \n        {\n            \"UserName\": \"user\"\n        } \n    ]\n}\n"
  },
  {
    "path": "linter/testdata/resources/uses_local_variables.tf",
    "content": "locals {\n    bucket_name = \"myprojectbucket\"\n}\n\nresource \"aws_s3_bucket\" \"my_bucket\" {\n    name = \"${local.bucket_name}\"\n}\n"
  },
  {
    "path": "linter/testdata/resources/uses_tf12_variables.tf",
    "content": "variable \"instance_type\" {\n  default = \"t2.micro\"\n}\n\nvariable \"ami\" {\n  default = \"ami-f2d3638a\"\n}\n\nvariable \"project\" {\n  default = \"demo\"\n}\n\nvariable \"list_variable\" {\n  default = [ \"foo\", \"bar\" ]\n}\n\nvariable \"default_tags\" {\n  default = {\n    project = \"demo\"\n    environment = \"test\"\n  }\n}\n\nvariable \"environment\" {\n  default = \"test\"\n}\n\nvariable \"department\" {}\n\nresource \"aws_instance\" \"first\" {\n  ami = var.ami\n  instance_type = var.instance_type\n  tags = {\n    project = var.project\n    environment = lookup(var.default_tags,\"environment\",\"dev\")\n    comment = var.list_variable[1]\n    department = var.department\n  }\n}\n"
  },
  {
    "path": "linter/testdata/resources/uses_variables.tf",
    "content": "variable \"instance_type\" {\n  default = \"t2.micro\"\n}\n\nvariable \"ami\" {\n  default = \"ami-f2d3638a\"\n}\n\nvariable \"project\" {\n  default = \"demo\"\n}\n\nvariable \"list_variable\" {\n  default = [ \"foo\", \"bar\" ]\n}\n\nvariable \"default_tags\" {\n  default = {\n    project = \"demo\"\n    environment = \"test\"\n  }\n}\n\nvariable \"environment\" {\n  default = \"test\"\n}\n\nvariable \"department\" {}\n\nresource \"aws_instance\" \"first\" {\n  ami = \"${var.ami}\"\n  instance_type = \"${var.instance_type}\"\n  tags = {\n    project = \"${var.project}\"\n    environment = \"${lookup(var.default_tags,\"environment\",\"dev\")}\"\n    comment = \"${var.list_variable[1]}\"\n    department = \"${var.department}\"\n  }\n}\n"
  },
  {
    "path": "linter/testdata/rules/aggregate.yml",
    "content": "version: 1\ndescription: Aggregate example\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_1\n    message: Must have subnets with 10.0.1.0 and 10.0.2.0 CIDR blocks\n    resource: aws_subnet\n    aggregate: true\n    assertions:\n      - some:\n          key: \"@\"\n          expressions: \n            - key: cidr_block\n              op: eq\n              value: \"10.0.1.0/24\"\n      - some:\n          key: \"@\"\n          expressions: \n            - key: cidr_block\n              op: eq\n              value: \"10.0.2.0/24\"\n\n  - id: TEST_2\n    message: Must have exactly two subnets\n    resource: aws_subnet\n    aggregate: true\n    assertions:\n      - key: \"@\"\n        op: eq\n        value: 2\n        value_type: size\n\n  - id: TEST_3\n    message: Every subnet must has a Name tag # doesn't need aggregate\n    resource: aws_subnet\n    aggregate: true\n    assertions:\n      - every:\n          key: \"@\"\n          expressions:\n            - key: tags\n              op: not-empty\n    severity: FAILURE\n"
  },
  {
    "path": "linter/testdata/rules/bad-format.yml",
    "content": "---\n- The rule object \n- should be an object\n- instead of an array\n"
  },
  {
    "path": "linter/testdata/rules/batch_definition.yml",
    "content": "version: 1\ndescription: Batch Privileged\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: BATCH_DEFINITION_PRIVILEGED\n    message: Batch Job Definition Container Properties should not have Privileged set to true\n    resource: aws_batch_job_definition\n    severity: WARNING\n    assertions:\n      - not:\n        - key: container_properties.privileged\n          op: is-true"
  },
  {
    "path": "linter/testdata/rules/cloudfront_access_logs.yml",
    "content": "version: 1\ndescription: Batch Privileged\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: CLOUDFRONT ACCESS LOGGING\n    message: CloudFront Distribution should enable access logging\n    resource: aws_cloudfront_distribution\n    severity: WARNING\n    assertions:\n      - key: \"logging_config\"\n        op: present"
  },
  {
    "path": "linter/testdata/rules/dms_endpoint_encryption.yml",
    "content": "\nversion: 1\ndescription: AWS DMS Endpoint Encryption\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: AWS_DMS_ENDPOINT_ENCRYPTION\n    message: AWS DMS Endpoint should have a kms key present\n    resource: aws_dms_endpoint\n    severity: WARNING\n    assertions:\n      - key: kms_key_arn\n        op: present"
  },
  {
    "path": "linter/testdata/rules/dynamic_block.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"dynamic_block.tf\"\nrules:\n  - id: NO_SSH_ACCESS\n    message: Testing\n    resource: aws_security_group\n    assertions:\n      - key: \"dynamic[*].for_each[]\"\n        op: not-contains\n        value: 22\n"
  },
  {
    "path": "linter/testdata/rules/ec2_public.yml",
    "content": "version: 1\ndescription: EC2 Public\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: EC2_SUBNET_MAP_PUBLIC\n    message: EC2 Subnet should not have MapPublicIpOnLaunch set to true\n    resource: aws_subnet\n    severity: WARNING\n    assertions:\n    - not:\n      - key: map_public_ip_on_launch\n        op: is-true"
  },
  {
    "path": "linter/testdata/rules/elasticache_encryption_rest.yml",
    "content": "version: 1\ndescription: Elasticache encryption at rest\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: ELASTICACHE_ENCRYPTION_REST\n    message: ElastiCache ReplicationGroup should have encryption enabled for at rest\n    resource: aws_elasticache_replication_group\n    severity: FAILURE\n    assertions:\n      - key: at_rest_encryption_enabled\n        op: is-true\n"
  },
  {
    "path": "linter/testdata/rules/elasticache_encryption_transit.yml",
    "content": "version: 1\ndescription: Elasticache encryption in transit\ntype: Terraform\nfiles:\n    - \"*.tf\"\nrules:\n  - id: ELASTICACHE_ENCRYPTION_TRANSIT\n    message: ElastiCache ReplicationGroup should have encryption enabled for in transit\n    resource: aws_elasticache_replication_group\n    severity: FAILURE\n    assertions:\n      - key: transit_encryption_enabled\n        op: is-true\n"
  },
  {
    "path": "linter/testdata/rules/emr_cluster_logs.yml",
    "content": "version: 1\ndescription: AWS EMR Cluster Logging\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: AWS_EMR_CLUSTER_LOGGING\n    message: AWS EMR Should have logging enabled\n    resource: aws_emr_cluster\n    severity: WARNING\n    assertions:\n      - key: log_uri\n        op: present\n    tags:\n      - emr"
  },
  {
    "path": "linter/testdata/rules/exclude_resource.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_1\n    message: Testing\n    resource: aws_instance\n    except:\n      - first\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n    tags:\n      - ec2\n\n"
  },
  {
    "path": "linter/testdata/rules/explicit_chars.yml",
    "content": "version: 1\ndescription: Explicit string check test\ntype: Terraform12\nfiles:\n  - \"*.tf\"\nrules:\n  - id: \"CHECK_FOR_COLON\"\n    message: \"Testing for key with a colon\"\n    resources:\n      - aws_s3_bucket_policy\n    severity: FAILURE\n    category: resource\n    assertions:\n      - some:\n          key: policy.Statement[]\n          expressions:\n            - key: Condition.Bool.\"aws:SecureTransport\"\n              op: eq\n              value: false\n"
  },
  {
    "path": "linter/testdata/rules/generic-csv.yml",
    "content": "version: 1\ndescription: Rules for users in CSV file\ntype: CSV\nfiles:\n  - \"*.csv\"\n\ncolumns:\n  - name: User\n  - name: Department\n\nrules:\n  - id: DEPARTMENT_REQUIRED\n    message: User must have a department\n    resource: row\n    assertions:\n      - key: Department\n        op: not-empty\n"
  },
  {
    "path": "linter/testdata/rules/generic-json.yml",
    "content": "version: 1\ndescription: Rules for users in a JSON file\ntype: JSON\nfiles:\n  - \"*.json\"\n\nresources:\n  - type: User\n    key: Users\n    id: UserName\n\nrules:\n  - id: DEPARTMENT_REQUIRED\n    message: User must have a department\n    resource: User\n    assertions:\n      - key: Department\n        op: present\n"
  },
  {
    "path": "linter/testdata/rules/generic-yaml.yml",
    "content": "version: 1\ndescription: Rules for generic YAML file\ntype: YAML\nfiles:\n  - \"*.config\"\n\n# For generic YAML linting, we need a list of resources\n# Each entry in the list describes the resource type, how to discover it in the file, and how to get its ID\n\nresources:\n  - type: widget\n    key: widgets[]\n    id: id\n  - type: gadget\n    key: gadgets[]\n    id: name\n  - type: contraption\n    key: other_stuff.contraptions[]\n    id: ids.serial_number\n\nrules:\n\n  - id: WIDGET_NAME\n    message: Widget needs a name\n    severity: FAILURE\n    resource: widget\n    assertions:\n      - key: name\n        op: present\n\n  - id: GADGET_COLOR\n    message: Gadget has missing or invalid color\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: color\n        op: in\n        value: red,blue,green\n\n  - id: GADGET_PROPERTIES\n    message: Gadget has missing properties\n    severity: FAILURE\n    resource: gadget\n    assertions:\n      - key: \"@\"\n        op: has-properties\n        value: name,color\n\n  - id: CONTRAPTION_SIZE\n    message: Contraption size should be less than 1000\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - key: size\n        op: lt\n        value: 1000\n        value_type: integer\n\n  - id: CONTRAPTION_LOCATIONS\n    message: Contraption location must have city\n    resource: contraption\n    severity: FAILURE\n    assertions:\n      - every:\n          key: locations\n          assertions:\n            - key: city\n              op: present\n\n"
  },
  {
    "path": "linter/testdata/rules/kinesis_kms_stream.yml",
    "content": "version: 1\ndescription:  Kinesis streams kms\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: KINESIS_STREAM_KMS\n    message: Kinesis streams should be associated with a kms key\n    resource: aws_kinesis_stream\n    severity: FAILURE\n    assertions:\n      - key: kms_key_id\n        op: present\n"
  },
  {
    "path": "linter/testdata/rules/kms_key_rotation.yml",
    "content": "version: 1\ndescription: AWS KMS Key Rotation\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: AWS_KMS_KEY_ROTATION\n    message: AWS KMS Key Rotation should be enabled\n    resource: aws_kms_key\n    severity: WARNING\n    assertions:\n      - key: enable_key_rotation\n        op: is-true\n    tags:\n      - kms"
  },
  {
    "path": "linter/testdata/rules/kubernetes.yml",
    "content": "version: 1\ndescription: Rules for Kubernetes\ntype: Kubernetes\nfiles:\n  - \"*.tf\"\nrules:\n"
  },
  {
    "path": "linter/testdata/rules/neptune_db_encryption.yml",
    "content": "version: 1\ndescription: Neptune DB Encryption\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: NEPTUNE_DB_ENCRYPTION\n    message: Neptune database cluster storage should have encryption enabled\n    resource: aws_neptune_cluster\n    severity: FAILURE\n    assertions:\n      - key: storage_encrypted\n        op: is-true\n"
  },
  {
    "path": "linter/testdata/rules/nullable_value.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"nullable_value.tf\"\nrules:\n\n  - id: NO_KEY_PAIR\n    message: Testing\n    resource: aws_instance\n    assertions:\n      - key: key_pair_name\n        op: absent\n"
  },
  {
    "path": "linter/testdata/rules/policy_variable.yml",
    "content": "version: 1\ndescription: Rules for Terraform here documents with variables\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_VARIABLE\n    message: Testing\n    resource: aws_iam_role\n    assertions:\n      - key: \"assume_role_policy.Statement[].Effect | [0]\"\n        op: eq\n        value: Allow\n"
  },
  {
    "path": "linter/testdata/rules/rds_publicly_available.yml",
    "content": "version: 1\ndescription: RDS Public Availability\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n  - id: RDS_PUBLIC_AVAILABILITY\n    message: RDS instance should not be publicly accessible\n    resource: aws_db_instance\n    severity: FAILURE\n    assertions:\n      - not:\n        - key: publicly_accessible\n          op: is-true\n"
  },
  {
    "path": "linter/testdata/rules/rules.yml",
    "content": "version: 1\ndescription: Rules to lint rules\ntype: LintRules\nfiles:\n  - \"*.yml\"\nrules:\n\n  - id: VALID_VERSION\n    message: RuleSet must have a supported version\n    resource: LintRuleSet\n    severity: WARNING\n    assertions:\n      - key: version\n        op: eq\n        value: 1\n\n  - id: HAS_RULES\n    message: RuleSet needs at least one rule\n    resource: LintRuleSet\n    severity: WARNING\n    assertions:\n      - key: rules\n        op: not-empty\n"
  },
  {
    "path": "linter/testdata/rules/sagemaker_endpoint_encryption.yml",
    "content": "version: 1\ndescription: AWS Sagemaker Endpoint Encryption\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: SAGEMAKER_ENDPOINT_ENCRYPTION\n    message: Sagemaker configuration should be encrypted\n    resource: aws_sagemaker_endpoint_configuration\n    severity: WARNING\n    assertions:\n      - key: kms_key_arn\n        op: present\n    tags:\n      - sagemaker"
  },
  {
    "path": "linter/testdata/rules/sagemaker_notebook_encryption.yml",
    "content": "version: 1\ndescription: AWS Sagemaker Notebook Encryption\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: SAGEMAKER_NOTEBOOK_ENCRYPTION\n    message: Sagemaker Notebook should be encrypted\n    resource: aws_sagemaker_notebook_instance\n    severity: WARNING\n    assertions:\n      - key: kms_key_id\n        op: present\n    tags:\n      - sagemaker"
  },
  {
    "path": "linter/testdata/rules/tagging.yml",
    "content": "---\nversion: 1\ndescription: Terraform v12 rules\ntype: Terraform12\nfiles:\n  - \"*.tf\"\n  - \"*.tfvars\"\nrules:\n  # This test rule is intended to recreate a reported bug, issue 112\n  - id: \"TAG_VALID\"\n    message: \"test: all data sources must have the data_class tag present.\"\n    resources:\n      - aws_db_instance\n      - aws_dynamodb_table\n      - aws_ebs_volume\n      - aws_efs_file_system\n      - aws_elasticsearch_domain\n      - aws_emr_cluste\n      - aws_kinesis_stream\n      - aws_rds_cluster\n      - aws_redshift_cluster\n      - aws_s3_bucket\n      - aws_sqs_queue\n    category: resource\n    severity: FAILURE\n    assertions:\n      - or:\n          - key: tags[0].data_class\n            op: in\n            value: public,internal,confidential\n          - key: tags.data_class\n            op: in\n            value: public,internal,confidential\n          # Fuzzy matching for nested modules that cant be resolved\n          - key: tags\n            op: contains\n            value: tags\n          - key: tags\n            op: contains\n            value: TAGS\n"
  },
  {
    "path": "linter/testdata/rules/terraform_bucket.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_1\n    message: Testing\n    resource: aws_s3_bucket\n    assertions:\n      - key: name\n        op: present\n    severity: WARNING\n"
  },
  {
    "path": "linter/testdata/rules/terraform_data.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: DATA_NOT_CONTAINS\n    message: Testing\n    resource: template_file\n    category: data\n    assertions:\n      - key: template\n        op: not-contains\n        value: \"/example/\" \n"
  },
  {
    "path": "linter/testdata/rules/terraform_instance.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_1\n    message: Testing\n    resource: aws_instance\n    assertions:\n      - key: instance_type\n        op: in\n        value: t2.micro,m3.medium\n    severity: WARNING\n    tags:\n      - ec2\n"
  },
  {
    "path": "linter/testdata/rules/terraform_module.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: MODULE_DESCRIPTION\n    message: Testing\n    resource: \"./modules/foo\"\n    category: module\n    assertions:\n      - key: description\n        op: present\n\n"
  },
  {
    "path": "linter/testdata/rules/terraform_policy.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: TEST_POLICY\n    message: Testing\n    resource: aws_iam_role\n    assertions:\n      - every:\n          key: assume_role_policy.Statement\n          expressions:\n            - key: Effect\n              op: eq\n              value: Deny\n    severity: FAILURE\n"
  },
  {
    "path": "linter/testdata/rules/terraform_provider.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: AWS_PROVIDER\n    message: Testing\n    resource: aws\n    category: provider\n    assertions:\n      - key: access_key\n        op: present\n      - key: secret_key\n        op: present\n      - key: region\n        op: present\n\n  - id: SAFE_PROVIDER\n    message: Testing\n    resource: safe\n    category: provider\n    assertions:\n      - key: password\n        op: absent\n\n"
  },
  {
    "path": "linter/testdata/rules/terraform_v12_variables.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: AMI_SET\n    message: Testing\n    resource: aws_instance\n    assertions:\n      - key: ami\n        op: eq\n        value: ami-f2d3638a\n"
  },
  {
    "path": "linter/testdata/rules/tf12_for_loop.yml",
    "content": "version: 1\ndescription: Rules for Terraform configuration files\ntype: Terraform\nfiles:\n  - \"*.tf\"\nrules:\n\n  - id: CIDR_SET\n    message: Testing\n    resource: aws_security_group\n    assertions:\n      - every:\n          key: \"ingress\"\n          expressions:\n            # this says that it either must be a private IP, or not have IP regex (eg sg string, interpolation)\n            - every:\n                key: cidr_blocks\n                expressions:\n                  - key: \"@\"\n                    op: contains\n                    value: \"/24\"\n"
  },
  {
    "path": "linter/testdata/rules/unknown.yml",
    "content": "version: 1\ndescription: Unsupported type\ntype: CloudFormation\nfiles:\n  - \"*.yml\"\nrules:\n\n"
  },
  {
    "path": "linter/tf12parser/README.md",
    "content": "Portions of files in this directory are originally sourced from https://github.com/liamg/tfsec,\ncopyright 2019 Liam Galvin.\n"
  },
  {
    "path": "linter/tf12parser/attribute.go",
    "content": "package tf12parser\n\nimport (\n\t\"github.com/hashicorp/hcl/v2\"\n\t\"github.com/hashicorp/hcl/v2/hclsyntax\"\n\t\"github.com/zclconf/go-cty/cty\"\n)\n\ntype Attribute struct {\n\thclAttribute *hclsyntax.Attribute\n\tctx          *hcl.EvalContext\n}\n\nfunc NewAttribute(attr *hclsyntax.Attribute, ctx *hcl.EvalContext) *Attribute {\n\treturn &Attribute{\n\t\thclAttribute: attr,\n\t\tctx:          ctx,\n\t}\n}\n\nfunc (attr *Attribute) IsLiteral() bool {\n\treturn len(attr.hclAttribute.Expr.Variables()) == 0\n}\n\nfunc (attr *Attribute) Type() cty.Type {\n\treturn attr.Value().Type()\n}\n\nfunc (attr *Attribute) Value() cty.Value {\n\tif attr == nil {\n\t\treturn cty.NilVal\n\t}\n\tctyVal, _ := attr.hclAttribute.Expr.Value(attr.ctx)\n\tif !ctyVal.IsKnown() {\n\t\treturn cty.NilVal\n\t}\n\treturn ctyVal\n}\n\nfunc (attr *Attribute) Range() Range {\n\treturn Range{\n\t\tFilename:  attr.hclAttribute.SrcRange.Filename,\n\t\tStartLine: attr.hclAttribute.SrcRange.Start.Line,\n\t\tEndLine:   attr.hclAttribute.SrcRange.End.Line,\n\t}\n}\n\nfunc (attr *Attribute) Name() string {\n\treturn attr.hclAttribute.Name\n}\n"
  },
  {
    "path": "linter/tf12parser/block.go",
    "content": "package tf12parser\n\nimport (\n\t\"strings\"\n\n\t\"github.com/hashicorp/hcl/v2\"\n\t\"github.com/hashicorp/hcl/v2/hclsyntax\"\n)\n\ntype Block struct {\n\thclBlock *hcl.Block\n\tctx      *hcl.EvalContext\n\tprefix   string\n}\n\ntype Blocks []*Block\n\nfunc (blocks Blocks) OfType(t string) Blocks {\n\tvar results []*Block\n\tfor _, block := range blocks {\n\t\tif block.Type() == t && block.prefix == \"\" {\n\t\t\tresults = append(results, block)\n\t\t}\n\t}\n\treturn results\n}\n\nfunc (blocks Blocks) RemoveDuplicates() Blocks {\n\tvar filtered Blocks\n\tfor _, block := range blocks {\n\t\tvar found bool\n\t\tfor _, current := range filtered {\n\t\t\tif current.Range() == block.Range() {\n\t\t\t\tfound = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tfiltered = append(filtered, block)\n\t\t}\n\t}\n\treturn filtered\n}\n\nfunc NewBlock(hclBlock *hcl.Block, ctx *hcl.EvalContext) *Block {\n\treturn &Block{\n\t\thclBlock: hclBlock,\n\t\tctx:      ctx,\n\t}\n}\n\nfunc (block *Block) body() *hclsyntax.Body {\n\treturn block.hclBlock.Body.(*hclsyntax.Body)\n}\n\nfunc (block *Block) Type() string {\n\treturn block.hclBlock.Type\n}\n\nfunc (block *Block) Labels() []string {\n\treturn block.hclBlock.Labels\n}\n\nfunc (block *Block) Range() Range {\n\tif block == nil || block.hclBlock == nil {\n\t\treturn Range{}\n\t}\n\tr := block.body().SrcRange\n\treturn Range{\n\t\tFilename:  r.Filename,\n\t\tStartLine: r.Start.Line,\n\t\tEndLine:   r.End.Line,\n\t}\n}\n\nfunc (block *Block) AllBlocks() []*Block {\n\tif block == nil || block.hclBlock == nil {\n\t\treturn nil\n\t}\n\tvar blocks []*Block\n\tfor _, child := range block.body().Blocks {\n\t\t\tblocks = append(blocks, NewBlock(child.AsHCLBlock(), block.ctx))\n\t}\n\treturn blocks\n}\n\nfunc (block *Block) GetBlock(name string) *Block {\n\tif block == nil || block.hclBlock == nil {\n\t\treturn nil\n\t}\n\tfor _, child := range block.body().Blocks {\n\t\tif child.Type == name {\n\t\t\treturn NewBlock(child.AsHCLBlock(), block.ctx)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (block *Block) GetBlocks(name string) Blocks {\n\tif block == nil || block.hclBlock == nil {\n\t\treturn nil\n\t}\n\tvar results []*Block\n\tfor _, child := range block.body().Blocks {\n\t\tif child.Type == name {\n\t\t\tresults = append(results, NewBlock(child.AsHCLBlock(), block.ctx))\n\t\t}\n\t}\n\treturn results\n}\n\nfunc (block *Block) GetAttributes() []*Attribute {\n\tvar results []*Attribute\n\tif block == nil || block.hclBlock == nil {\n\t\treturn nil\n\t}\n\tfor _, attr := range block.body().Attributes {\n\t\tresults = append(results, NewAttribute(attr, block.ctx))\n\t}\n\treturn results\n}\n\nfunc (block *Block) GetAttribute(name string) *Attribute {\n\tif block == nil || block.hclBlock == nil {\n\t\treturn nil\n\t}\n\tfor _, attr := range block.body().Attributes {\n\t\tif attr.Name == name {\n\t\t\treturn NewAttribute(attr, block.ctx)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (block *Block) Name() string {\n\tvar prefix string\n\tif block.Type() != \"resource\" {\n\t\tprefix = block.Type() + \".\"\n\t}\n\tif block.prefix != \"\" {\n\t\tprefix = block.prefix + \".\" + prefix\n\t}\n\treturn prefix + strings.Join(block.Labels(), \".\")\n}\n"
  },
  {
    "path": "linter/tf12parser/parser.go",
    "content": "package tf12parser\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/hashicorp/hcl/v2\"\n\t\"github.com/hashicorp/hcl/v2/hclparse\"\n\t\"github.com/hashicorp/terraform/lang\"\n\t\"github.com/zclconf/go-cty/cty\"\n)\n\nconst maxContextIterations = 32\n\n// Parser is a tool for parsing terraform templates at a given file system location\ntype Parser struct {\n\thclParser *hclparse.Parser\n\tfiles     map[string]bool\n}\n\n// New creates a new Parser\nfunc New() *Parser {\n\treturn &Parser{\n\t\thclParser: hclparse.NewParser(),\n\t\tfiles:     make(map[string]bool),\n\t}\n}\n\nfunc (parser *Parser) ParseMany(paths []string) (Blocks, error) {\n\tfor _, path := range paths {\n\t\tparser.hclParser.ParseHCLFile(path)\n\t}\n\n\tvar blocks hcl.Blocks\n\tfor _, file := range parser.hclParser.Files() {\n\t\tfileBlocks, err := parser.parseFile(file)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tblocks = append(blocks, fileBlocks...)\n\t}\n\n\tinputVars := make(map[string]cty.Value)\n\t// TODO add .tfvars values to inputVars\n\n\tallBlocks, _ := parser.buildEvaluationContext(blocks, paths[len(paths)-1], inputVars, true)\n\treturn allBlocks.RemoveDuplicates(), nil\n}\n\n// ParseDirectory recursively parses all terraform files within a given directory\nfunc (parser *Parser) ParseDirectory(path string) (Blocks, error) {\n\n\tif err := parser.recursivelyParseDirectory(path); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar blocks hcl.Blocks\n\n\tfor _, file := range parser.hclParser.Files() {\n\t\tfileBlocks, err := parser.parseFile(file)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tblocks = append(blocks, fileBlocks...)\n\t}\n\n\tinputVars := make(map[string]cty.Value)\n\t// TODO add .tfvars values to inputVars\n\n\tallBlocks, _ := parser.buildEvaluationContext(blocks, path, inputVars, true)\n\treturn allBlocks.RemoveDuplicates(), nil\n}\n\nfunc (parser *Parser) ParseFile(path string) (Blocks, error) {\n\n\tvar blocks hcl.Blocks\n\n\tfile, _ := parser.hclParser.ParseHCLFile(path)\n\n\tfileBlocks, err := parser.parseFile(file)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tblocks = append(blocks, fileBlocks...)\n\n\tinputVars := make(map[string]cty.Value)\n\t// TODO add .tfvars values to inputVars\n\n\tallBlocks, _ := parser.buildEvaluationContext(blocks, path, inputVars, true)\n\treturn allBlocks.RemoveDuplicates(), nil\n}\n\nfunc (parser *Parser) parseFile(file *hcl.File) (hcl.Blocks, error) {\n\n\tcontents, diagnostics := file.Body.Content(terraformSchema)\n\tif diagnostics != nil && diagnostics.HasErrors() {\n\t\treturn nil, diagnostics\n\t}\n\n\tif contents == nil {\n\t\treturn nil, fmt.Errorf(\"file contents is empty\")\n\t}\n\n\treturn contents.Blocks, nil\n}\n\nfunc (parser *Parser) recursivelyParseDirectory(path string) error {\n\n\tfiles, err := ioutil.ReadDir(path)\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, file := range files {\n\t\tif strings.HasPrefix(file.Name(), \".\") { //ignore dotfiles (including .terraform!)\n\t\t\tcontinue\n\t\t}\n\t\tfullPath := filepath.Join(path, file.Name())\n\t\tif exists := parser.files[fullPath]; exists {\n\t\t\tcontinue\n\t\t}\n\t\tparser.files[fullPath] = true\n\t\tif file.IsDir() {\n\t\t\tif err := parser.recursivelyParseDirectory(fullPath); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t} else if strings.HasSuffix(file.Name(), \".tf\") {\n\t\t\t_, diagnostics := parser.hclParser.ParseHCLFile(fullPath)\n\t\t\tif diagnostics != nil && diagnostics.HasErrors() {\n\t\t\t\treturn diagnostics\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// BuildEvaluationContext creates an *hcl.EvalContext by parsing values for all terraform variables (where available) then interpolating values into resource, local and data blocks until all possible values can be constructed\nfunc (parser *Parser) buildEvaluationContext(blocks hcl.Blocks, path string, inputVars map[string]cty.Value, isRoot bool) (Blocks, *hcl.EvalContext) {\n\tscope := lang.Scope{\n\t\t//TODO: I'm not 100% sure this will be right in all cases, but the single test around this passes\n\t\tBaseDir: \".\",\n\t}\n\tctx := &hcl.EvalContext{\n\t\tVariables: make(map[string]cty.Value),\n\t\tFunctions: scope.Functions(),\n}\n\n\tctx.Variables[\"module\"] = cty.ObjectVal(make(map[string]cty.Value))\n\tctx.Variables[\"resource\"] = cty.ObjectVal(make(map[string]cty.Value))\n\n\tmoduleBlocks := make(map[string]Blocks)\n\n\tfor i := 0; i < maxContextIterations; i++ {\n\n\t\tctx.Variables[\"var\"] = parser.getValuesByBlockType(ctx, blocks, \"variable\", inputVars)\n\t\tctx.Variables[\"local\"] = parser.getValuesByBlockType(ctx, blocks, \"locals\", nil)\n\t\tctx.Variables[\"provider\"] = parser.getValuesByBlockType(ctx, blocks, \"provider\", nil)\n\t\tresources := parser.getValuesByBlockType(ctx, blocks, \"resource\", nil)\n\t\tfor key, resource := range resources.AsValueMap() {\n\t\t\tctx.Variables[key] = resource\n\t\t}\n\t\tctx.Variables[\"data\"] = parser.getValuesByBlockType(ctx, blocks, \"data\", nil)\n\n\t\tif isRoot {\n\t\t\tctx.Variables[\"output\"] = parser.getValuesByBlockType(ctx, blocks, \"output\", nil)\n\t\t} else {\n\t\t\toutputs := parser.getValuesByBlockType(ctx, blocks, \"output\", nil)\n\t\t\tfor key, val := range outputs.AsValueMap() {\n\t\t\t\tctx.Variables[key] = val\n\t\t\t}\n\t\t}\n\n\t\tfor _, moduleBlock := range blocks.OfType(\"module\") {\n\t\t\tif len(moduleBlock.Labels) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmoduleMap := ctx.Variables[\"module\"].AsValueMap()\n\t\t\tif moduleMap == nil {\n\t\t\t\tmoduleMap = make(map[string]cty.Value)\n\t\t\t}\n\t\t\tmoduleName := moduleBlock.Labels[0]\n\t\t\tmoduleBlocks[moduleName], moduleMap[moduleName] = parser.parseModuleBlock(moduleBlock, ctx, path) // todo return parsed blocks here too\n\t\t\tctx.Variables[\"module\"] = cty.ObjectVal(moduleMap)\n\t\t}\n\n\t\t// todo check of ctx has changed since last iteration - break if not\n\t}\n\n\tvar localBlocks []*Block\n\tfor _, block := range blocks {\n\t\tlocalBlocks = append(localBlocks, NewBlock(block, ctx))\n\t}\n\n\tfor moduleName, blocks := range moduleBlocks {\n\t\tfor _, block := range blocks {\n\t\t\tblock.prefix = fmt.Sprintf(\"module.%s\", moduleName)\n\t\t\tlocalBlocks = append(localBlocks, block)\n\t\t}\n\t}\n\treturn localBlocks, ctx\n}\n\nfunc (parser *Parser) parseModuleBlock(block *hcl.Block, parentContext *hcl.EvalContext, rootPath string) (Blocks, cty.Value) {\n\n\tif len(block.Labels) == 0 {\n\t\treturn nil, cty.NilVal\n\t}\n\n\tinputVars := make(map[string]cty.Value)\n\n\tvar source string\n\tattrs, _ := block.Body.JustAttributes()\n\tfor _, attr := range attrs {\n\n\t\tinputVars[attr.Name], _ = attr.Expr.Value(parentContext)\n\n\t\tif attr.Name == \"source\" {\n\t\t\tsourceVal, _ := attr.Expr.Value(parentContext)\n\t\t\tif sourceVal.Type() == cty.String {\n\t\t\t\tsource = sourceVal.AsString()\n\t\t\t}\n\t\t}\n\t}\n\n\tif source == \"\" {\n\t\treturn nil, cty.NilVal\n\t}\n\n\tif !strings.HasPrefix(source, \"./\") && !strings.HasPrefix(source, \"../\") {\n\t\t// TODO support module registries/github etc.\n\t\treturn nil, cty.NilVal\n\t}\n\n\tpath := filepath.Join(rootPath, source)\n\n\tsubParser := New()\n\n\tif err := subParser.recursivelyParseDirectory(path); err != nil {\n\t\treturn nil, cty.NilVal\n\t}\n\n\tvar blocks []*hcl.Block\n\n\tfor _, file := range subParser.hclParser.Files() {\n\t\tfileBlocks, err := subParser.parseFile(file)\n\t\tif err != nil {\n\t\t\treturn nil, cty.NilVal\n\t\t}\n\t\tblocks = append(blocks, fileBlocks...)\n\t}\n\n\tchildModules, ctx := subParser.buildEvaluationContext(blocks, path, inputVars, false)\n\n\treturn childModules, cty.ObjectVal(ctx.Variables)\n}\n\n// returns true if all evaluations were successful\nfunc (parser *Parser) readValues(ctx *hcl.EvalContext, block *hcl.Block) cty.Value {\n\n\tvalues := make(map[string]cty.Value)\n\n\tattributes, diagnostics := block.Body.JustAttributes()\n\tif diagnostics != nil && diagnostics.HasErrors() {\n\t\treturn cty.NilVal\n\t}\n\n\tfor _, attribute := range attributes {\n\t\tval, _ := attribute.Expr.Value(ctx)\n\t\tvalues[attribute.Name] = val\n\t}\n\n\treturn cty.ObjectVal(values)\n}\n\n// returns true if all evaluations were successful\nfunc (parser *Parser) getValuesByBlockType(ctx *hcl.EvalContext, blocks hcl.Blocks, blockType string, inputVars map[string]cty.Value) cty.Value {\n\n\tblocksOfType := blocks.OfType(blockType)\n\tvalues := make(map[string]cty.Value)\n\n\tfor _, block := range blocksOfType {\n\n\t\tswitch block.Type {\n\t\tcase \"variable\": // variables are special in that their value comes from the \"default\" attribute\n\t\t\tif len(block.Labels) < 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tattributes, _ := block.Body.JustAttributes()\n\t\t\tif attributes == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif override, exists := inputVars[block.Labels[0]]; exists {\n\t\t\t\tvalues[block.Labels[0]] = override\n\t\t\t} else if def, exists := attributes[\"default\"]; exists {\n\t\t\t\tvalues[block.Labels[0]], _ = def.Expr.Value(ctx)\n\t\t\t}\n\t\tcase \"output\":\n\t\t\tif len(block.Labels) < 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tattributes, _ := block.Body.JustAttributes()\n\t\t\tif attributes == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif def, exists := attributes[\"value\"]; exists {\n\t\t\t\tvalues[block.Labels[0]], _ = def.Expr.Value(ctx)\n\t\t\t}\n\t\tcase \"locals\":\n\t\t\tfor key, val := range parser.readValues(ctx, block).AsValueMap() {\n\t\t\t\tvalues[key] = val\n\t\t\t}\n\t\tcase \"provider\", \"module\":\n\t\t\tif len(block.Labels) < 1 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvalues[block.Labels[0]] = parser.readValues(ctx, block)\n\t\tcase \"resource\", \"data\":\n\n\t\t\tif len(block.Labels) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tblockMap, ok := values[block.Labels[0]]\n\t\t\tif !ok {\n\t\t\t\tvalues[block.Labels[0]] = cty.ObjectVal(make(map[string]cty.Value))\n\t\t\t\tblockMap = values[block.Labels[0]]\n\t\t\t}\n\n\t\t\tvalueMap := blockMap.AsValueMap()\n\t\t\tif valueMap == nil {\n\t\t\t\tvalueMap = make(map[string]cty.Value)\n\t\t\t}\n\n\t\t\tvalueMap[block.Labels[1]] = parser.readValues(ctx, block)\n\t\t\tvalues[block.Labels[0]] = cty.ObjectVal(valueMap)\n\t\t}\n\n\t}\n\n\treturn cty.ObjectVal(values)\n\n}\n"
  },
  {
    "path": "linter/tf12parser/parser_test.go",
    "content": "package tf12parser\n\nimport (\n\t\"io/ioutil\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/zclconf/go-cty/cty\"\n\n\t\"github.com/stretchr/testify/assert\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc Test_BasicParsing(t *testing.T) {\n\tparser := New()\n\n\tpath := createTestFile(\"test.tf\", `\n\nlocals {\n\tproxy = var.cats_mother\n}\n\nvariable \"cats_mother\" {\n\tdefault = \"boots\"\n}\n\nprovider \"cats\" {\n\n}\n\nresource \"cats_cat\" \"mittens\" {\n\tname = \"mittens\"\n\tspecial = true\n}\n\nresource \"cats_kitten\" \"the-great-destroyer\" {\n\tname = \"the great destroyer\"\n    parent = cats_cat.mittens.name\n}\n\ndata \"cats_cat\" \"the-cats-mother\" {\n\tname = local.proxy\n}\n\nvariable \"project\" {\n  default = \"demo\"\n}\n\nvariable \"default_tags\" {\n  default = {\n    project = \"demo\"\n    environment = \"test\"\n  }\n}\n\nresource \"aws_instance\" \"first\" {\n  tags = {\n    project = var.project\n    environment = lookup(var.default_tags,\"environment\",\"dev\")\n  }\n}\n`)\n\n\tblocks, err := parser.ParseDirectory(filepath.Dir(path))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\t// variable\n\tvariables := blocks.OfType(\"variable\")\n\trequire.Len(t, variables, 3)\n\tassert.Equal(t, \"variable\", variables[0].Type())\n\trequire.Len(t, variables[0].Labels(), 1)\n\tassert.Equal(t, \"cats_mother\", variables[0].Labels()[0])\n\tdefaultVal := variables[0].GetAttribute(\"default\")\n\trequire.NotNil(t, defaultVal)\n\tassert.Equal(t, cty.String, defaultVal.Value().Type())\n\tassert.Equal(t, \"boots\", defaultVal.Value().AsString())\n\n\t// provider\n\tproviderBlocks := blocks.OfType(\"provider\")\n\trequire.Len(t, providerBlocks, 1)\n\tassert.Equal(t, \"provider\", providerBlocks[0].Type())\n\trequire.Len(t, providerBlocks[0].Labels(), 1)\n\tassert.Equal(t, \"cats\", providerBlocks[0].Labels()[0])\n\n\t// resources\n\tresourceBlocks := blocks.OfType(\"resource\")\n\trequire.Len(t, resourceBlocks, 3)\n\trequire.Len(t, resourceBlocks[0].Labels(), 2)\n\n\tassert.Equal(t, \"resource\", resourceBlocks[0].Type())\n\tassert.Equal(t, \"cats_cat\", resourceBlocks[0].Labels()[0])\n\tassert.Equal(t, \"mittens\", resourceBlocks[0].Labels()[1])\n\n\tassert.Equal(t, \"mittens\", resourceBlocks[0].GetAttribute(\"name\").Value().AsString())\n\tassert.True(t, resourceBlocks[0].GetAttribute(\"special\").Value().True())\n\n\tassert.Equal(t, \"resource\", resourceBlocks[1].Type())\n\tassert.Equal(t, \"cats_kitten\", resourceBlocks[1].Labels()[0])\n\tassert.Equal(t, \"the great destroyer\", resourceBlocks[1].GetAttribute(\"name\").Value().AsString())\n\tassert.Equal(t, \"mittens\", resourceBlocks[1].GetAttribute(\"parent\").Value().AsString())\n\n\t// New Tests\n\tassert.Equal(t, \"resource\", resourceBlocks[2].Type())\n\tassert.Equal(t, \"aws_instance\", resourceBlocks[2].Labels()[0])\n\tassert.Equal(t, \"first\", resourceBlocks[2].Labels()[1])\n\tassert.Equal(t, \"demo\", resourceBlocks[2].GetAttribute(\"tags\").Value().AsValueMap()[\"project\"].AsString())\n\tassert.Equal(t, true, resourceBlocks[2].GetAttribute(\"tags\").Value().AsValueMap()[\"environment\"].IsKnown())\n\tassert.Equal(t, \"test\", resourceBlocks[2].GetAttribute(\"tags\").Value().AsValueMap()[\"environment\"].AsString())\n\n\n\t// data\n\tdataBlocks := blocks.OfType(\"data\")\n\trequire.Len(t, dataBlocks, 1)\n\trequire.Len(t, dataBlocks[0].Labels(), 2)\n\n\tassert.Equal(t, \"data\", dataBlocks[0].Type())\n\tassert.Equal(t, \"cats_cat\", dataBlocks[0].Labels()[0])\n\tassert.Equal(t, \"the-cats-mother\", dataBlocks[0].Labels()[1])\n\n\tassert.Equal(t, \"boots\", dataBlocks[0].GetAttribute(\"name\").Value().AsString())\n}\n\nfunc Test_Modules(t *testing.T) {\n\n\tpath := createTestFileWithModule(`\nmodule \"my-mod\" {\n\tsource = \"../module\"\n\tinput = \"ok\"\n}\n\noutput \"result\" {\n\tvalue = module.my-mod.result\n}\n`,\n\t\t`\nvariable \"input\" {\n\tdefault = \"?\"\n}\n\noutput \"result\" {\n\tvalue = var.input\n}\n`,\n\t)\n\n\tparser := New()\n\n\tblocks, err := parser.ParseDirectory(path)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tmodules := blocks.OfType(\"module\")\n\trequire.Len(t, modules, 1)\n\tmodule := modules[0]\n\tassert.Equal(t, \"module\", module.Type())\n\tassert.Equal(t, \"module.my-mod\", module.Name())\n\tinputAttr := module.GetAttribute(\"input\")\n\trequire.NotNil(t, inputAttr)\n\trequire.Equal(t, cty.String, inputAttr.Value().Type())\n\tassert.Equal(t, \"ok\", inputAttr.Value().AsString())\n\n\toutputs := blocks.OfType(\"output\")\n\trequire.Len(t, outputs, 1)\n\toutput := outputs[0]\n\tassert.Equal(t, \"output.result\", output.Name())\n\tvalAttr := output.GetAttribute(\"value\")\n\trequire.NotNil(t, valAttr)\n\trequire.Equal(t, cty.String, valAttr.Type())\n\tassert.Equal(t, \"ok\", valAttr.Value().AsString())\n}\n\nfunc createTestFile(filename, contents string) string {\n\tdir, err := ioutil.TempDir(os.TempDir(), \"tfsec\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tpath := filepath.Join(dir, filename)\n\tif err := ioutil.WriteFile(path, []byte(contents), 0755); err != nil {\n\t\tpanic(err)\n\t}\n\treturn path\n}\n\nfunc createTestFileWithModule(contents string, moduleContents string) string {\n\tdir, err := ioutil.TempDir(os.TempDir(), \"tfsec\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\trootPath := filepath.Join(dir, \"main\")\n\tmodulePath := filepath.Join(dir, \"module\")\n\n\tif err := os.Mkdir(rootPath, 0755); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := os.Mkdir(modulePath, 0755); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := ioutil.WriteFile(filepath.Join(rootPath, \"main.tf\"), []byte(contents), 0755); err != nil {\n\t\tpanic(err)\n\t}\n\n\tif err := ioutil.WriteFile(filepath.Join(modulePath, \"main.tf\"), []byte(moduleContents), 0755); err != nil {\n\t\tpanic(err)\n\t}\n\n\treturn rootPath\n}\n"
  },
  {
    "path": "linter/tf12parser/range.go",
    "content": "package tf12parser\n\nimport \"fmt\"\n\n// Range describes an area of code, including the filename it is present in and the lin numbers the code occupies\ntype Range struct {\n\tFilename  string\n\tStartLine int\n\tEndLine   int\n}\n\n// String creates a human-readable summary of the range\nfunc (r *Range) String() string {\n\tif r == nil {\n\t\treturn \"unknown\"\n\t}\n\tif r.StartLine != r.EndLine {\n\t\treturn fmt.Sprintf(\"%s:%d-%d\", r.Filename, r.StartLine, r.EndLine)\n\t}\n\treturn fmt.Sprintf(\"%s:%d\", r.Filename, r.StartLine)\n}\n"
  },
  {
    "path": "linter/tf12parser/schema.go",
    "content": "package tf12parser\n\nimport \"github.com/hashicorp/hcl/v2\"\n\n// lifted from terraform 0.12 source\nvar terraformSchema = &hcl.BodySchema{\n\tBlocks: []hcl.BlockHeaderSchema{\n\t\t{\n\t\t\tType: \"terraform\",\n\t\t},\n\t\t{\n\t\t\tType:       \"provider\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"variable\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType: \"locals\",\n\t\t},\n\t\t{\n\t\t\tType:       \"output\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"module\",\n\t\t\tLabelNames: []string{\"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"resource\",\n\t\t\tLabelNames: []string{\"type\", \"name\"},\n\t\t},\n\t\t{\n\t\t\tType:       \"data\",\n\t\t\tLabelNames: []string{\"type\", \"name\"},\n\t\t},\n\t},\n}\n"
  },
  {
    "path": "linter/yaml_resource_loader.go",
    "content": "package linter\n\nimport (\n\t\"github.com/stelligent/config-lint/assertion\"\n\t\"path/filepath\"\n)\n\n// YAMLResourceLoader loads a list of Resource objects based on the list of ResourceConfig objects\ntype YAMLResourceLoader struct {\n\tResources []assertion.ResourceConfig\n}\n\nfunc extractYAMLResourceID(expression string, properties interface{}) string {\n\tresourceID := \"None\"\n\tresult, err := assertion.SearchData(expression, properties)\n\tif err == nil {\n\t\tresourceID, _ = result.(string)\n\t}\n\treturn resourceID\n}\n\n// Load converts a text file into a collection of Resource objects\nfunc (l YAMLResourceLoader) Load(filename string) (FileResources, error) {\n\tloaded := FileResources{\n\t\tResources: make([]assertion.Resource, 0),\n\t}\n\tyamlResources, err := loadYAML(filename)\n\tif err != nil {\n\t\treturn loaded, err\n\t}\n\tfor _, document := range yamlResources {\n\t\tfor _, resourceConfig := range l.Resources {\n\t\t\tmatches, err := assertion.SearchData(resourceConfig.Key, document)\n\t\t\tif err != nil {\n\t\t\t\treturn loaded, nil\n\t\t\t}\n\t\t\tsliceOfProperties, ok := matches.([]interface{})\n\t\t\tif ok {\n\t\t\t\tfor _, element := range sliceOfProperties {\n\t\t\t\t\tproperties := element.(map[string]interface{})\n\t\t\t\t\tproperties[\"__file__\"] = filename\n\t\t\t\t\tproperties[\"__dir__\"] = filepath.Dir(filename)\n\t\t\t\t\tresource := assertion.Resource{\n\t\t\t\t\t\tID:         extractYAMLResourceID(resourceConfig.ID, properties),\n\t\t\t\t\t\tType:       resourceConfig.Type,\n\t\t\t\t\t\tProperties: properties,\n\t\t\t\t\t\tFilename:   filename,\n\t\t\t\t\t}\n\t\t\t\t\tloaded.Resources = append(loaded.Resources, resource)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn loaded, nil\n}\n\n// PostLoad does no additional processing for a YAMLResourceLoader\nfunc (l YAMLResourceLoader) PostLoad(r FileResources) ([]assertion.Resource, error) {\n\treturn r.Resources, nil\n}\n"
  },
  {
    "path": "linter/yaml_resource_loader_test.go",
    "content": "package linter\n\nimport (\n\t\"bytes\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestYAMLLinterValidate(t *testing.T) {\n\toptions := Options{\n\t\tTags:    []string{},\n\t\tRuleIDs: []string{},\n\t}\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-yaml.yml\", t)\n\tfilenames := []string{\"./testdata/resources/generic.config\"}\n\tloader := YAMLResourceLoader{Resources: ruleSet.Resources}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\treport, err := linter.Validate(ruleSet, options)\n\tassert.Nil(t, err, \"Expecting Validate to run without error\")\n\tassert.Equal(t, 17, len(report.ResourcesScanned), \"Expecting Validate to scan 17 resources\")\n\tassert.Equal(t, 1, len(report.FilesScanned), \"Expecting Validate to scan 1 file\")\n\tassert.Equal(t, 3, len(report.Violations), \"Expecting Validate to find 3 violations\")\n}\n\nfunc TestYAMLLinterSearch(t *testing.T) {\n\truleSet := loadRulesForTest(\"./testdata/rules/generic-yaml.yml\", t)\n\tfilenames := []string{\"./testdata/resources/generic.config\"}\n\tloader := YAMLResourceLoader{Resources: ruleSet.Resources}\n\tlinter := FileLinter{Filenames: filenames, ValueSource: TestingValueSource{}, Loader: loader}\n\tvar b bytes.Buffer\n\tlinter.Search(ruleSet, \"name\", &b)\n\tassert.Contains(t, b.String(), \"gadget\", \"Expecting TestYAMLLinterSearch to find string in output\")\n}\n"
  }
]