diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index c00ff41..812a8ca 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -25,6 +25,7 @@ - [ ] I have added tests to cover my changes - [ ] I have updated the documentation accordingly - [ ] This PR is a result of pair or mob programming +- [ ] If I have used the 'skip-trivy-package' label I have done so responsibly and in the knowledge that this is being fixed as part of a separate ticket/PR. --- diff --git a/.github/actions/trivy-iac/action.yaml b/.github/actions/trivy-iac/action.yaml new file mode 100644 index 0000000..583f935 --- /dev/null +++ b/.github/actions/trivy-iac/action.yaml @@ -0,0 +1,18 @@ +name: "Trivy IaC Scan" +description: "Scan Terraform IaC using Trivy" +runs: + using: "composite" + steps: + - name: "Trivy Terraform IaC Scan" + shell: bash + run: | + components_exit_code=0 + modules_exit_code=0 + + ./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/components || components_exit_code=$? + ./scripts/terraform/trivy-scan.sh --mode iac ./infrastructure/terraform/modules || modules_exit_code=$? + + if [ $components_exit_code -ne 0 ] || [ $modules_exit_code -ne 0 ]; then + echo "Trivy misconfigurations detected." + exit 1 + fi diff --git a/.github/actions/trivy-package/action.yaml b/.github/actions/trivy-package/action.yaml new file mode 100644 index 0000000..d6ee4a3 --- /dev/null +++ b/.github/actions/trivy-package/action.yaml @@ -0,0 +1,16 @@ +name: "Trivy Package Scan" +description: "Scan project packages using Trivy" +runs: + using: "composite" + steps: + - name: "Trivy Package Scan" + shell: bash + run: | + exit_code=0 + + ./scripts/terraform/trivy-scan.sh --mode package . || exit_code=$? + + if [ $exit_code -ne 0 ]; then + echo "Trivy has detected package vulnerablilites. Please refer to https://nhsd-confluence.digital.nhs.uk/spaces/RIS/pages/1257636917/PLAT-KOP-012+-+Trivy+Pipeline+Vulnerability+Scanning+Exemption" + exit 1 + fi diff --git a/.github/workflows/cicd-1-pull-request.yaml b/.github/workflows/cicd-1-pull-request.yaml index 2aeaafb..0208518 100644 --- a/.github/workflows/cicd-1-pull-request.yaml +++ b/.github/workflows/cicd-1-pull-request.yaml @@ -28,6 +28,8 @@ jobs: terraform_version: ${{ steps.variables.outputs.terraform_version }} version: ${{ steps.variables.outputs.version }} does_pull_request_exist: ${{ steps.pr_exists.outputs.does_pull_request_exist }} + pr_number: ${{ steps.pr_exists.outputs.pr_number }} + skip_trivy_package: ${{ steps.skip_trivy.outputs.skip_trivy_package }} steps: - name: "Checkout code" uses: actions/checkout@v4 @@ -51,12 +53,38 @@ jobs: run: | branch_name=${GITHUB_HEAD_REF:-$(echo $GITHUB_REF | sed 's#refs/heads/##')} echo "Current branch is '$branch_name'" - if gh pr list --head $branch_name | grep -q .; then - echo "Pull request exists" + + pr_json=$(gh pr list --head "$branch_name" --state open --json number --limit 1) + pr_number=$(echo "$pr_json" | jq -r '.[0].number // empty') + + if [[ -n "$pr_number" ]]; then + echo "Pull request exists: #$pr_number" echo "does_pull_request_exist=true" >> $GITHUB_OUTPUT + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT else echo "Pull request doesn't exist" echo "does_pull_request_exist=false" >> $GITHUB_OUTPUT + echo "pr_number=" >> $GITHUB_OUTPUT + fi + - name: "Determine if Trivy package scan should be skipped" + id: skip_trivy + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ steps.pr_exists.outputs.pr_number }} + run: | + if [[ -z "$PR_NUMBER" ]]; then + echo "No pull request detected; Trivy package scan will run." + echo "skip_trivy_package=false" >> $GITHUB_OUTPUT + exit 0 + fi + + labels=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels[].name') + echo "Labels on PR #$PR_NUMBER: $labels" + + if echo "$labels" | grep -Fxq 'skip-trivy-package'; then + echo "skip_trivy_package=true" >> $GITHUB_OUTPUT + else + echo "skip_trivy_package=false" >> $GITHUB_OUTPUT fi - name: "List variables" run: | @@ -80,6 +108,7 @@ jobs: build_epoch: "${{ needs.metadata.outputs.build_epoch }}" nodejs_version: "${{ needs.metadata.outputs.nodejs_version }}" python_version: "${{ needs.metadata.outputs.python_version }}" + skip_trivy_package: ${{ needs.metadata.outputs.skip_trivy_package == 'true' }} terraform_version: "${{ needs.metadata.outputs.terraform_version }}" version: "${{ needs.metadata.outputs.version }}" secrets: inherit diff --git a/.github/workflows/stage-1-commit.yaml b/.github/workflows/stage-1-commit.yaml index 226ee5f..94aaf48 100644 --- a/.github/workflows/stage-1-commit.yaml +++ b/.github/workflows/stage-1-commit.yaml @@ -23,6 +23,10 @@ on: description: "Python version, set by the CI/CD pipeline workflow" required: true type: string + skip_trivy_package: + description: "Skip Trivy package scan when true" + type: boolean + default: false terraform_version: description: "Terraform version, set by the CI/CD pipeline workflow" required: true @@ -146,21 +150,39 @@ jobs: uses: actions/checkout@v4 - name: "Lint Terraform" uses: ./.github/actions/lint-terraform - trivy: - name: "Trivy Scan" + trivy-iac: + name: "Trivy IaC Scan" + permissions: + contents: read runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 10 needs: detect-terraform-changes if: needs.detect-terraform-changes.outputs.terraform_changed == 'true' steps: - name: "Checkout code" uses: actions/checkout@v4 - name: "Setup ASDF" - uses: asdf-vm/actions/setup@v4 + uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302 + - name: "Perform Setup" + uses: ./.github/actions/setup + - name: "Trivy IaC Scan" + uses: ./.github/actions/trivy-iac + trivy-package: + if: ${{ !inputs.skip_trivy_package }} + name: "Trivy Package Scan" + permissions: + contents: read + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + - name: "Setup ASDF" + uses: asdf-vm/actions/setup@1902764435ca0dd2f3388eea723a4f92a4eb8302 - name: "Perform Setup" uses: ./.github/actions/setup - - name: "Trivy Scan" - uses: ./.github/actions/trivy + - name: "Trivy Package Scan" + uses: ./.github/actions/trivy-package count-lines-of-code: name: "Count lines of code" runs-on: ubuntu-latest diff --git a/.tool-versions b/.tool-versions index 63bba7d..389f264 100644 --- a/.tool-versions +++ b/.tool-versions @@ -13,8 +13,8 @@ python 3.13.2 # The section below is reserved for Docker image versions. # TODO: Move this section - consider using a different file for the repository template dependencies. -# docker/ghcr.io/anchore/grype v0.92.2@sha256:651e558f9ba84f2a790b3449c8a57cbbf4f34e004f7d3f14ae8f8cbeede4cd33 # SEE: https://github.com/anchore/grype/pkgs/container/grype -# docker/ghcr.io/anchore/syft v1.26.0@sha256:de078f51704a213906970b1475edd6006b8af50aa159852e125518237487b8c6 # SEE: https://github.com/anchore/syft/pkgs/container/syft +# docker/ghcr.io/anchore/grype v0.104.3@sha256:d340f4f8b3b7e6e72a6c9c0152f25402ed8a2d7375dba1dfce4e53115242feb6 # SEE: https://github.com/anchore/grype/pkgs/container/grype +# docker/ghcr.io/anchore/syft v1.39.0@sha256:6f13bb010923c33fb197047c8f88888e77071bd32596b3f605d62a133e493ce4 # SEE: https://github.com/anchore/syft/pkgs/container/syft # docker/ghcr.io/gitleaks/gitleaks:v8.24.0@sha256:b8e9bf46893c2f20e10bfb4b2e783adaef519dea981b01ca6221ac325e836040 # SEE: https://github.com/gitleaks/gitleaks/pkgs/container/gitleaks # docker/ghcr.io/igorshubovych/markdownlint-cli v0.37.0@sha256:fb3e79946fce78e1cde84d6798c6c2a55f2de11fc16606a40d49411e281d950d # SEE: https://github.com/igorshubovych/markdownlint-cli/pkgs/container/markdownlint-cli # docker/ghcr.io/make-ops-tools/gocloc latest@sha256:6888e62e9ae693c4ebcfed9f1d86c70fd083868acb8815fe44b561b9a73b5032 # SEE: https://github.com/make-ops-tools/gocloc/pkgs/container/gocloc diff --git a/lambdas/rewrite-origin-branch-requests/package-lock.json b/lambdas/rewrite-origin-branch-requests/package-lock.json index b81d68e..6a4c407 100644 --- a/lambdas/rewrite-origin-branch-requests/package-lock.json +++ b/lambdas/rewrite-origin-branch-requests/package-lock.json @@ -26,13 +26,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -162,19 +164,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -189,111 +193,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", - "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/parser": { - "version": "7.25.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.3.tgz", - "integrity": "sha512-iLTJKDbJ4hMvFPgQwwsVoxtHyWpKKPBrxkANrSYewDPaPpT5py5yeVkgPIJ7XYXhndxJpaA3PyALSXQ7u8e/Dw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.2" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -525,14 +445,15 @@ } }, "node_modules/@babel/template": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", - "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.25.0", - "@babel/types": "^7.25.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -557,14 +478,14 @@ } }, "node_modules/@babel/types": { - "version": "7.25.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.2.tgz", - "integrity": "sha512-YTnYtra7W9e6/oAZEHj0bJehPRUlLH9/fbpT5LfB0NhQXyALCRkRs3zH9v07IYhkgpqX6Z78FnuccZr/l4Fs4Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1233,10 +1154,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1467,10 +1389,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2590,13 +2513,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -2723,10 +2648,11 @@ "dev": true }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -2928,10 +2854,11 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3284,15 +3211,6 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/scripts/config/trivy.yaml b/scripts/config/trivy.yaml index a4eff46..29707db 100644 --- a/scripts/config/trivy.yaml +++ b/scripts/config/trivy.yaml @@ -4,3 +4,4 @@ exit-code: 1 # When issues are found scan: skip-files: - "**/.terraform/**/*" + - "**/node_modules/**/*" diff --git a/scripts/githooks/check-file-format.sh b/scripts/githooks/check-file-format.sh index d7c9474..632e536 100755 --- a/scripts/githooks/check-file-format.sh +++ b/scripts/githooks/check-file-format.sh @@ -66,9 +66,11 @@ function main() { ;; esac - if command -v editorconfig > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then + if command -v editorconfig-checker > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then + echo "Running editorconfig-checker natively" filter="$filter" dry_run_opt="${dry_run_opt:-}" run-editorconfig-natively else + echo "Running editorconfig-checker in Docker" filter="$filter" dry_run_opt="${dry_run_opt:-}" run-editorconfig-in-docker fi } @@ -80,7 +82,7 @@ function main() { function run-editorconfig-natively() { # shellcheck disable=SC2046,SC2086 - editorconfig \ + editorconfig-checker \ --exclude '.git/' $dry_run_opt $($filter) } @@ -101,7 +103,7 @@ function run-editorconfig-in-docker() { docker run --rm --platform linux/amd64 \ --volume "$PWD":/check \ "$image" \ - sh -c "ec --exclude '.git/' $dry_run_opt \$($filter) /dev/null" + sh -c "set -x; ec --exclude '.git/' $dry_run_opt \$($filter) /dev/null" } # ============================================================================== diff --git a/scripts/terraform/trivy-scan.sh b/scripts/terraform/trivy-scan.sh new file mode 100755 index 0000000..1565623 --- /dev/null +++ b/scripts/terraform/trivy-scan.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash + +# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/NHSDigital/nhs-notify-repository-template). Raise a PR instead. + +set -euo pipefail + +function usage() { + cat <<'EOF' +Usage: ./scripts/terraform/trivy-scan.sh --mode [directory] + +Options: + --mode, -m Scan type to run. Accepts "iac" or "package" (required). + --help, -h Show this message. + [directory] Directory to scan. Defaults to the repository root. + +Environment variables: + FORCE_USE_DOCKER=true Force execution through Docker even if Trivy is installed locally. + VERBOSE=true Enable bash -x tracing. +EOF +} + +function main() { + cd "$(git rev-parse --show-toplevel)" + + local scan_mode="" + local dir_to_scan="." + + while [[ $# -gt 0 ]]; do + case "$1" in + --mode|-m) + if [[ $# -lt 2 ]]; then + echo "Error: --mode requires an argument." >&2 + usage + exit 1 + fi + scan_mode="$2" + shift 2 + ;; + --help|-h) + usage + exit 0 + ;; + --) + shift + break + ;; + -*) + echo "Unknown option: $1" >&2 + usage + exit 1 + ;; + *) + dir_to_scan="$1" + shift + ;; + esac + done + + if [[ $# -gt 0 ]]; then + dir_to_scan="$1" + fi + + if [[ -z "$scan_mode" ]]; then + echo "Error: --mode must be provided (iac|package)." >&2 + usage + exit 1 + fi + + case "$scan_mode" in + iac|package) + ;; + *) + echo "Error: unknown mode '$scan_mode'. Expected 'iac' or 'package'." >&2 + usage + exit 1 + ;; + esac + + if command -v trivy > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then + run-trivy-natively "$scan_mode" "$dir_to_scan" + else + run-trivy-in-docker "$scan_mode" "$dir_to_scan" + fi +} + +function run-trivy-natively() { + local scan_mode="$1" + local dir_to_scan="$2" + + echo "Trivy found locally, running natively" + echo "Running Trivy ($scan_mode) on directory: $dir_to_scan" + + if execute-trivy-command "$scan_mode" "$dir_to_scan"; then + check-trivy-status 0 + else + local status=$? + check-trivy-status "$status" + fi +} + +function run-trivy-in-docker() { + # shellcheck disable=SC1091 + source ./scripts/docker/docker.lib.sh + + local scan_mode="$1" + local dir_to_scan="$2" + + # shellcheck disable=SC2155 + local image=$(name=aquasec/trivy docker-get-image-version-and-pull) + + echo "Trivy not found locally, running in Docker Container" + echo "Running Trivy ($scan_mode) on directory: $dir_to_scan" + + if execute-trivy-in-docker "$image" "$scan_mode" "$dir_to_scan"; then + check-trivy-status 0 + else + local status=$? + check-trivy-status "$status" + fi +} + +function execute-trivy-command() { + local scan_mode="$1" + local dir_to_scan="$2" + + if [[ "$scan_mode" == "iac" ]]; then + trivy config \ + --config scripts/config/trivy.yaml \ + --tf-exclude-downloaded-modules \ + "$dir_to_scan" + else + trivy \ + --config scripts/config/trivy.yaml \ + fs "$dir_to_scan" \ + --scanners vuln \ + --severity HIGH,CRITICAL \ + --include-dev-deps + fi +} + +function execute-trivy-in-docker() { + local image="$1" + local scan_mode="$2" + local dir_to_scan="$3" + + if [[ "$scan_mode" == "iac" ]]; then + docker run --rm --platform linux/amd64 \ + --volume "$PWD":/workdir \ + --workdir /workdir \ + "$image" \ + config \ + --config scripts/config/trivy.yaml \ + --tf-exclude-downloaded-modules \ + "$dir_to_scan" + else + docker run --rm --platform linux/amd64 \ + --volume "$PWD":/workdir \ + --workdir /workdir \ + "$image" \ + --config scripts/config/trivy.yaml \ + fs "$dir_to_scan" \ + --scanners vuln \ + --severity HIGH,CRITICAL \ + --include-dev-deps + fi +} + +function check-trivy-status() { + local status="$1" + + if [[ "$status" -eq 0 ]]; then + echo "Trivy completed successfully." + return 0 + fi + + echo "Trivy found issues." + exit "$status" +} + +function is-arg-true() { + if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then + return 0 + else + return 1 + fi +} + +# ============================================================================== + +is-arg-true "${VERBOSE:-false}" && set -x + +main "$@" + +exit 0 diff --git a/scripts/terraform/trivy.sh b/scripts/terraform/trivy.sh deleted file mode 100755 index 93caabd..0000000 --- a/scripts/terraform/trivy.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -# WARNING: Please DO NOT edit this file! It is maintained in the Repository Template (https://github.com/NHSDigital/nhs-notify-repository-template). Raise a PR instead. - -set -euo pipefail - -# TFSec command wrapper. It will run the command natively if TFSec is -# installed, otherwise it will run it in a Docker container. -# Run tfsec for security checks on Terraform code. -# -# Usage: -# $ ./trivy.sh [directory] -# ============================================================================== - -function main() { - - cd "$(git rev-parse --show-toplevel)" - - local dir_to_scan=${1:-.} - - if command -v trivy > /dev/null 2>&1 && ! is-arg-true "${FORCE_USE_DOCKER:-false}"; then - # shellcheck disable=SC2154 - run-trivy-natively "$dir_to_scan" - else - run-trivy-in-docker "$dir_to_scan" - fi -} - -# Run trivy on the specified directory. -# Arguments: -# $1 - Directory to scan -function run-trivy-natively() { - - local dir_to_scan="$1" - - echo "Trivy found locally, running natively" - - echo "Running Trivy on directory: $dir_to_scan" - trivy config \ - --config scripts/config/trivy.yaml \ - --tf-exclude-downloaded-modules \ - "${dir_to_scan}" - - check-trivy-status -} - -# Check the exit status of tfsec. -function check-trivy-status() { - - if [ $? -eq 0 ]; then - echo "Trivy completed successfully." - else - echo "Trivy found issues." - exit 1 - fi -} - -function run-trivy-in-docker() { - - # shellcheck disable=SC1091 - source ./scripts/docker/docker.lib.sh - local dir_to_scan="$1" - - # shellcheck disable=SC2155 - local image=$(name=aquasec/trivy docker-get-image-version-and-pull) - # shellcheck disable=SC2086 - echo "Trivy not found locally, running in Docker Container" - echo "Running Trivy on directory: $dir_to_scan" - docker run --rm --platform linux/amd64 \ - --volume "$PWD":/workdir \ - --workdir /workdir \ - "$image" \ - config \ - --config scripts/config/trivy.yaml \ - --tf-exclude-downloaded-modules \ - "${dir_to_scan}" - check-trivy-status -} -# ============================================================================== - -function is-arg-true() { - - if [[ "$1" =~ ^(true|yes|y|on|1|TRUE|YES|Y|ON)$ ]]; then - return 0 - else - return 1 - fi -} - -# ============================================================================== - -is-arg-true "${VERBOSE:-false}" && set -x - -main "$@" - -exit 0