From da3b6d62b23f899e298802d64b638dc1ff5d9e68 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 13:57:25 +0000 Subject: [PATCH 01/13] wip --- package-lock.json | 1034 +++++++++++++++++++++++++++++- package.json | 3 +- scripts/download_latest_trivy.sh | 66 ++ scripts/install_cosign.sh | 79 +++ 4 files changed, 1180 insertions(+), 2 deletions(-) create mode 100755 scripts/download_latest_trivy.sh create mode 100755 scripts/install_cosign.sh diff --git a/package-lock.json b/package-lock.json index 827a984..e004734 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@devcontainers/cli": "^0.84.0" + "@devcontainers/cli": "^0.84.0", + "@tufjs/cli": "^0.4.1" } }, "node_modules/@devcontainers/cli": { @@ -23,6 +24,1037 @@ "engines": { "node": ">=20.0.0" } + }, + "node_modules/@gar/promise-retry": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", + "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", + "license": "MIT", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/agent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^11.2.1", + "socks-proxy-agent": "^8.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", + "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@npmcli/redact": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", + "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@oclif/color": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/@oclif/color/-/color-1.0.13.tgz", + "integrity": "sha512-/2WZxKCNjeHlQogCs1VBtJWlPXjwWke/9gMrwsVsrUt00g2V6LUBvwgwrxhrXepjOmq4IZ5QeNbpDMEOUlx/JA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.2.1", + "chalk": "^4.1.0", + "strip-ansi": "^6.0.1", + "supports-color": "^8.1.1", + "tslib": "^2" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@oclif/core": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.0.tgz", + "integrity": "sha512-T52mrztDvj7RXiURdA+6vd1B4yeYVpHF6DH/RshqEwIA6R63dhe4YoIBYWJi0H3LhCQTUTL84uWadrJAImu/vg==", + "license": "MIT", + "dependencies": { + "ansi-escapes": "^4.3.2", + "ansis": "^3.17.0", + "clean-stack": "^3.0.1", + "cli-spinners": "^2.9.2", + "debug": "^4.4.3", + "ejs": "^3.1.10", + "get-package-type": "^0.1.0", + "indent-string": "^4.0.0", + "is-wsl": "^2.2.0", + "lilconfig": "^3.1.3", + "minimatch": "^10.2.4", + "semver": "^7.7.3", + "string-width": "^4.2.3", + "supports-color": "^8", + "tinyglobby": "^0.2.14", + "widest-line": "^3.1.0", + "wordwrap": "^1.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@oclif/plugin-help": { + "version": "6.2.38", + "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.38.tgz", + "integrity": "sha512-aTVQ8qPy5kD/Neq2B4OEo2joukHWdEabTMHfQyXtsagW1O2MvhM58+JUWADvieX67OSjSXseD6f6O/e5SA2N/Q==", + "license": "MIT", + "dependencies": { + "@oclif/core": "^4" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", + "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/@tufjs/cli": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tufjs/cli/-/cli-0.4.1.tgz", + "integrity": "sha512-LVfLmIDj8GaDr5h614XP/xc2KSkSU9XbOTprsKUtz4XmbzXNaxCwMm1u90eFP29PB66MKb/BGCiAUcEf75UScQ==", + "license": "MIT", + "dependencies": { + "@oclif/color": "^1.0.13", + "@oclif/core": "^4", + "@oclif/plugin-help": "^6", + "make-fetch-happen": "^15.0.1", + "tuf-js": "4.1.0" + }, + "bin": { + "tuf": "bin/run" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/@tufjs/models": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", + "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^10.1.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ansis": { + "version": "3.17.0", + "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", + "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/cacache": { + "version": "20.0.4", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", + "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^5.0.0", + "fs-minipass": "^3.0.0", + "glob": "^13.0.0", + "lru-cache": "^11.1.0", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^7.0.2", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", + "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", + "license": "MIT", + "dependencies": { + "escape-string-regexp": "4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-minipass": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", + "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/make-fetch-happen": { + "version": "15.0.5", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz", + "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==", + "license": "ISC", + "dependencies": { + "@gar/promise-retry": "^1.0.0", + "@npmcli/agent": "^4.0.0", + "@npmcli/redact": "^4.0.0", + "cacache": "^20.0.1", + "http-cache-semantics": "^4.1.1", + "minipass": "^7.0.2", + "minipass-fetch": "^5.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", + "ssri": "^13.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", + "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-fetch": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", + "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^2.0.0", + "minizlib": "^3.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + }, + "optionalDependencies": { + "iconv-lite": "^0.7.2" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", + "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", + "license": "ISC", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", + "license": "MIT", + "dependencies": { + "minipass": "^7.1.2" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ssri": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", + "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tuf-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", + "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", + "license": "MIT", + "dependencies": { + "@tufjs/models": "4.1.0", + "debug": "^4.4.3", + "make-fetch-happen": "^15.0.1" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "license": "MIT", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" } } } diff --git a/package.json b/package.json index 44a8da5..efb687e 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "license": "ISC", "description": "", "dependencies": { - "@devcontainers/cli": "^0.84.0" + "@devcontainers/cli": "^0.84.0", + "@tufjs/cli": "^0.4.1" } } diff --git a/scripts/download_latest_trivy.sh b/scripts/download_latest_trivy.sh new file mode 100755 index 0000000..13235ca --- /dev/null +++ b/scripts/download_latest_trivy.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +VERSION="v0.69.3" +RELEASE_NUMBER="${VERSION#v}" +BASE_URL="https://github.com/aquasecurity/trivy/releases/download/${VERSION}" +ARCHIVE="trivy_${RELEASE_NUMBER}_Linux-64bit.tar.gz" +BUNDLE="${ARCHIVE}.sigstore.json" +CHECKSUM_FILE="${ARCHIVE}.sha256" +CERT_IDENTITY="https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/${VERSION}" +OUTPUT_DIR="${1:-$PWD}" + +usage() { + cat <<'EOF' +Usage: download_latest_trivy.sh [output_dir] + +Downloads Trivy v0.69.3, its sigstore bundle, and checksum into output_dir (default: current directory), +then verifies the checksum and the sigstore bundle, following +https://github.com/aquasecurity/trivy/blob/main/docs/getting-started/signature-verification.md. +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +for cmd in curl cosign sha256sum; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "Error: $cmd is required but not found in PATH" >&2 + exit 1 + fi +done + +mkdir -p "$OUTPUT_DIR" +pushd "$OUTPUT_DIR" >/dev/null +trap 'popd >/dev/null' EXIT + +download() { + local url="${1}" dest="${2}" + echo "Downloading ${dest} ..." + curl -fsSL "${url}" -o "${dest}" +} + +set_downloads() { + download "${BASE_URL}/${ARCHIVE}" "${ARCHIVE}" + download "${BASE_URL}/${BUNDLE}" "${BUNDLE}" + download "${BASE_URL}/${CHECKSUM_FILE}" "${CHECKSUM_FILE}" +} + +set_downloads + +if sha256sum -c "${CHECKSUM_FILE}"; then + echo "Checksum verification passed" +else + echo "Checksum verification failed" >&2 + exit 1 +fi + +cosign verify-blob-attestation "${ARCHIVE}" \ + --bundle "${BUNDLE}" \ + --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ + --certificate-identity "${CERT_IDENTITY}" + +echo "Sigstore verification passed" +echo "Trivy ${VERSION} download verified in ${OUTPUT_DIR}" diff --git a/scripts/install_cosign.sh b/scripts/install_cosign.sh new file mode 100755 index 0000000..9299443 --- /dev/null +++ b/scripts/install_cosign.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +set -euo pipefail + +DEFAULT_INSTALL_DIR="/usr/local/bin" +INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" +REQUESTED_VERSION="${1:-latest}" +OS="$(uname -s)" +ARCH="$(uname -m)" +API_URL="https://api.github.com/repos/sigstore/cosign/releases" + +usage() { + cat <<'EOF' +Usage: install_cosign.sh [version] + +Downloads the requested cosign release (default: latest) for Linux amd64, verifies +its SHA256 checksum, and installs it into $INSTALL_DIR (override via INSTALL_DIR env var). +EOF +} + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + usage + exit 0 +fi + +if [[ "$OS" != "Linux" ]]; then + echo "Error: This installer currently supports Linux only" >&2 + exit 1 +fi + +case "$ARCH" in + x86_64|amd64) + BINARY_NAME="cosign-linux-amd64" + ;; + *) + echo "Error: Unsupported architecture $ARCH" >&2 + exit 1 + ;; +esac + +for cmd in curl sha256sum install; do + if ! command -v "$cmd" >/dev/null 2>&1; then + echo "Error: $cmd is required but not found in PATH" >&2 + exit 1 + fi +done + +get_latest_tag() { + curl -fsSL "$API_URL/latest" | awk -F'"' '/tag_name/ {print $4; exit}' +} + +VERSION="$REQUESTED_VERSION" +if [[ "$VERSION" == "latest" ]]; then + VERSION="$(get_latest_tag)" +fi + +if [[ -z "$VERSION" ]]; then + echo "Error: Unable to determine cosign version" >&2 + exit 1 +fi + +BASE_URL="https://github.com/sigstore/cosign/releases/download/${VERSION}" +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT + +BIN_PATH="$TMP_DIR/${BINARY_NAME}" +SHA_PATH="$TMP_DIR/${BINARY_NAME}.sha256" + +curl -fsSL "${BASE_URL}/${BINARY_NAME}" -o "$BIN_PATH" +curl -fsSL "${BASE_URL}/${BINARY_NAME}.sha256" -o "$SHA_PATH" + +pushd "$TMP_DIR" >/dev/null +sha256sum -c "${BINARY_NAME}.sha256" +popd >/dev/null + +install -m 0755 "$BIN_PATH" "${INSTALL_DIR}/cosign" + +"${INSTALL_DIR}/cosign" version + +echo "cosign ${VERSION} installed to ${INSTALL_DIR}" From 5b9ae00d684b47c4d39df84381992ad6ab2808a6 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 17:01:53 +0000 Subject: [PATCH 02/13] add cosign --- .devcontainer/Dockerfile | 10 +++++++--- .tool-versions | 1 + scripts/install_cosign.sh | 37 +++++++++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 0e7ee62..535aeab 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -66,9 +66,9 @@ RUN git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets && \ USER vscode -ENV PATH="/home/vscode/.asdf/shims/:$PATH:/workspaces/eps-devcontainers/node_modules/.bin" +ENV PATH="/home/vscode/.asdf/shims:/home/vscode/.local/bin:$PATH:/workspaces/eps-devcontainers/node_modules/.bin" RUN \ - echo 'PATH="/home/vscode/.asdf/shims/:$PATH:/workspaces/eps-devcontainers/node_modules/.bin"' >> ~/.bashrc; \ + echo 'PATH="/home/vscode/.asdf/shims:/home/vscode/.local/bin:$PATH:/workspaces/eps-devcontainers/node_modules/.bin"' >> ~/.bashrc; \ echo '. <(asdf completion bash)' >> ~/.bashrc; \ echo '# Install Ruby Gems to ~/gems' >> ~/.bashrc; \ echo 'export GEM_HOME="$HOME/gems"' >> ~/.bashrc; \ @@ -83,7 +83,8 @@ RUN asdf plugin add python; \ asdf plugin add actionlint; \ asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git; \ asdf plugin add trivy https://github.com/zufardhiyaulhaq/asdf-trivy.git; \ - asdf plugin add yq https://github.com/sudermanjr/asdf-yq.git + asdf plugin add yq https://github.com/sudermanjr/asdf-yq.git; \ + asdf plugin add golang WORKDIR /workspaces/eps-devcontainers @@ -94,5 +95,8 @@ COPY .tool-versions /home/vscode/.tool-versions RUN asdf install python; \ asdf install +COPY scripts/install_cosign.sh /tmp/install_cosign.sh +RUN INSTALL_DIR=/home/vscode/.local/bin /tmp/install_cosign.sh + RUN git-secrets --register-aws --global && \ git-secrets --add-provider --global -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt diff --git a/.tool-versions b/.tool-versions index 1aed182..327b156 100644 --- a/.tool-versions +++ b/.tool-versions @@ -7,3 +7,4 @@ actionlint 1.7.10 ruby 3.3.0 trivy 0.69.3 yq 4.52.2 +golang 1.24.13 diff --git a/scripts/install_cosign.sh b/scripts/install_cosign.sh index 9299443..f5ae297 100755 --- a/scripts/install_cosign.sh +++ b/scripts/install_cosign.sh @@ -3,6 +3,7 @@ set -euo pipefail DEFAULT_INSTALL_DIR="/usr/local/bin" INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" +mkdir -p "$INSTALL_DIR" REQUESTED_VERSION="${1:-latest}" OS="$(uname -s)" ARCH="$(uname -m)" @@ -13,7 +14,7 @@ usage() { Usage: install_cosign.sh [version] Downloads the requested cosign release (default: latest) for Linux amd64, verifies -its SHA256 checksum, and installs it into $INSTALL_DIR (override via INSTALL_DIR env var). +its signature, and installs it into $INSTALL_DIR (override via INSTALL_DIR env var). EOF } @@ -37,7 +38,7 @@ case "$ARCH" in ;; esac -for cmd in curl sha256sum install; do +for cmd in curl openssl install go asdf; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "Error: $cmd is required but not found in PATH" >&2 exit 1 @@ -45,7 +46,9 @@ for cmd in curl sha256sum install; do done get_latest_tag() { - curl -fsSL "$API_URL/latest" | awk -F'"' '/tag_name/ {print $4; exit}' + local response + response="$(curl -fsSL "$API_URL/latest")" + awk -F'"' '/tag_name/ {print $4; exit}' <<<"$response" } VERSION="$REQUESTED_VERSION" @@ -63,15 +66,37 @@ TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT BIN_PATH="$TMP_DIR/${BINARY_NAME}" -SHA_PATH="$TMP_DIR/${BINARY_NAME}.sha256" +SIGSTORE_PATH="$TMP_DIR/${BINARY_NAME}-kms.sigstore.json" +ARTIFACT_PATH="$TMP_DIR/artifact.pub" +DECODED_SIGSTORE_PATH="$TMP_DIR/cosign-kms.sig.decoded" +echo "downloading ${BINARY_NAME} version ${VERSION} from ${BASE_URL}" curl -fsSL "${BASE_URL}/${BINARY_NAME}" -o "$BIN_PATH" -curl -fsSL "${BASE_URL}/${BINARY_NAME}.sha256" -o "$SHA_PATH" +echo "downloading sigstore signature" +curl -fsSL "${BASE_URL}/${BINARY_NAME}-kms.sigstore.json" -o "$SIGSTORE_PATH" +# install tuf-client +go install github.com/theupdateframework/go-tuf/cmd/tuf-client@latest +asdf reshim golang + +# setup tuf-client +SIGSTORE_ROOT_PATH="$TMP_DIR/sigstore-root.json" +curl -o "$SIGSTORE_ROOT_PATH" https://raw.githubusercontent.com/sigstore/root-signing/refs/heads/main/metadata/root_history/10.root.json +tuf-client init https://tuf-repo-cdn.sigstore.dev "$SIGSTORE_ROOT_PATH" + +tuf-client get https://tuf-repo-cdn.sigstore.dev artifact.pub > "$ARTIFACT_PATH" + +cat "$SIGSTORE_PATH" | jq -r .messageSignature.signature | base64 -d > "$DECODED_SIGSTORE_PATH" pushd "$TMP_DIR" >/dev/null -sha256sum -c "${BINARY_NAME}.sha256" +echo "verifying signature with artifact.pub" +openssl dgst -sha256 -verify "$ARTIFACT_PATH" -signature "$DECODED_SIGSTORE_PATH" "$BIN_PATH" popd >/dev/null +echo "verifying signature with cosign verify-blob" +chmod +x "$BIN_PATH" +${BIN_PATH} verify-blob --bundle "${SIGSTORE_PATH}" --key "$ARTIFACT_PATH" "$BIN_PATH" + + install -m 0755 "$BIN_PATH" "${INSTALL_DIR}/cosign" "${INSTALL_DIR}/cosign" version From c3fe81acd5155d0c474157ddbb49a3b8c163e999 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 17:26:16 +0000 Subject: [PATCH 03/13] install trivy --- .devcontainer/Dockerfile | 2 + .tool-versions | 1 - LICENSE | 222 +++++++- README.md | 488 +++++------------- contrib/asff.tpl | 161 ++++++ contrib/gitlab-codequality.tpl | 103 ++++ contrib/gitlab.tpl | 112 ++++ contrib/html.tpl | 148 ++++++ contrib/junit.tpl | 75 +++ scripts/install_cosign.sh | 15 +- ...nload_latest_trivy.sh => install_trivy.sh} | 41 +- 11 files changed, 959 insertions(+), 409 deletions(-) create mode 100644 contrib/asff.tpl create mode 100644 contrib/gitlab-codequality.tpl create mode 100644 contrib/gitlab.tpl create mode 100644 contrib/html.tpl create mode 100644 contrib/junit.tpl rename scripts/{download_latest_trivy.sh => install_trivy.sh} (64%) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 535aeab..e047cfe 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -96,7 +96,9 @@ RUN asdf install python; \ asdf install COPY scripts/install_cosign.sh /tmp/install_cosign.sh +COPY scripts/install_trivy.sh /tmp/install_trivy.sh RUN INSTALL_DIR=/home/vscode/.local/bin /tmp/install_cosign.sh +RUN INSTALL_DIR=/home/vscode/.local/bin /tmp/install_trivy.sh RUN git-secrets --register-aws --global && \ git-secrets --add-provider --global -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt diff --git a/.tool-versions b/.tool-versions index 327b156..5cd8921 100644 --- a/.tool-versions +++ b/.tool-versions @@ -5,6 +5,5 @@ shellcheck 0.11.0 direnv 2.37.1 actionlint 1.7.10 ruby 3.3.0 -trivy 0.69.3 yq 4.52.2 golang 1.24.13 diff --git a/LICENSE b/LICENSE index 0ba95e8..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,201 @@ -MIT License - -Crown Copyright (c) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 388b233..756d7d6 100644 --- a/README.md +++ b/README.md @@ -1,375 +1,147 @@ -EPS DEV CONTAINERS -================== - -## Index -- [Introduction](#introduction) -- [Using the images](#using-the-images) - - [Project setup](#project-setup) - - [Getting image name and version in GitHub Actions from local config](#getting-image-name-and-version-in-github-actions) - - [Using images in GitHub Actions](#using-images-in-github-actions) - - [Using local or pull request images in Visual Studio Code and GitHub Actions](#using-local-or-pull-request-images-in-visual-studio-code-and-github-actions) -- [Common Makefile targets](#common-makefile-targets) - - [Defined Targets](#targets) - -- [Project structure](#project-structure) -- [Pull requests and merge to main process](#pull-requests-and-merge-to-main-process) -- [Release workflow](#release-workflow) -- [Local testing](#local-testing) - - [Building images](#building-images) - - [Scanning images](#scanning-images) - - [Interactive shell on image](#interactive-shell-on-image) -- [Generating a .trivyignore file](#generating-a-trivyignore-file) -- [Cleaning up unused container images](#cleaning-up-unused-container-images) - -# Introduction -This repository contains code to build VS Code devcontainers that can be used as a base image for all EPS projects. -Images are built for AMD64 and ARM64, and a manifest file is created that can be pulled for both architectures. This is then pushed to GitHub Container Registry and an attestation created that can be used to verify the images before being used. -Images are built using https://github.com/devcontainers/cli. - -We build a base image based on mcr.microsoft.com/devcontainers/base:ubuntu-22.04 that other images are then based on - -The base image contains - - latest os packages - - asdf - - aws cli - - aws sam cli - - It installs the following dev container features - - docker outside of docker - - GitHub CLI - -As the vscode user the following also happens - -asdf install and setup for these so they are available globally as vscode user - - shellcheck - - direnv - - actionlint - - ruby (for GitHub Pages) - - Trivy - - yq - -Install and setup git-secrets - -# Using the images -## Project setup -In each EPS project, `.devcontainer/Dockerfile` should be set to -``` -ARG IMAGE_NAME=node_24_python_3_14 -ARG IMAGE_VERSION=latest -FROM ghcr.io/nhsdigital/eps-devcontainers/${IMAGE_NAME}:${IMAGE_VERSION} - -USER root -# specify DOCKER_GID to force container docker group id to match host -RUN if [ -n "${DOCKER_GID}" ]; then \ - if ! getent group docker; then \ - groupadd -g "${DOCKER_GID}" docker; \ - else \ - groupmod -g "${DOCKER_GID}" docker; \ - fi && \ - usermod -aG docker vscode; \ - fi -``` -`.devcontainer/devcontainer.json` should be set to: -``` -{ - "name": "eps-common-workflows", - "build": { - "dockerfile": "Dockerfile", - "context": "..", - "args": { - "DOCKER_GID": "${env:DOCKER_GID:}", - "IMAGE_NAME": "node_24_python_3_14", - "IMAGE_VERSION": "local-build", - "USER_UID": "${localEnv:USER_ID:}", - "USER_GID": "${localEnv:GROUP_ID:}" - }, - "updateRemoteUserUID": false, - }, - "postAttachCommand": "git-secrets --register-aws; git-secrets --add-provider -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt", - "mounts": [ - "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind", - "source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind", - "source=${env:HOME}${env:USERPROFILE}/.gnupg,target=/home/vscode/.gnupg,type=bind", - "source=${env:HOME}${env:USERPROFILE}/.npmrc,target=/home/vscode/.npmrc,type=bind" - ], - "containerUser": "vscode", - "remoteEnv": { - "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" - }, - "features": {}, - "customizations": { - .... - } -} -``` -Note - this file will be used in GitHub workflows to calculate the version of container to use in builds, so it must be valid JSON (no comments). -The name should be changed to match the name of the project. -IMAGE_NAME and IMAGE_VERSION should be changed as appropriate. -You should not need to add any features as these are already baked into the image +
+ -## Getting image name and version in GitHub Actions -This shared workflow should be used in GitHub Actions wherever you need to get the dev container name or tag. +[![GitHub Release][release-img]][release] +[![Test][test-img]][test] +[![Go Report Card][go-report-img]][go-report] +[![License: Apache-2.0][license-img]][license] +[![GitHub Downloads][github-downloads-img]][release] +![Docker Pulls][docker-pulls] -verify_published_from_main_image should be set to false for testing pull request images. +[📖 Documentation][docs] +
-``` - get_config_values: - uses: NHSDigital/eps-common-workflows/.github/workflows/get-repo-config.yml@8404cf6e3a61ac8de4d1644e175e288aa4965815 - with: - verify_published_from_main_image: false -``` -## Using images in GitHub Actions -To use the image in GitHub Actions, you should first verify the attestation of the image and reference the image by the digest -For CI and release pipelines, you should set verify_published_from_main_image to ensure that only images published from main are used. -``` -jobs: - my_job_name: - runs-on: ubuntu-22.04 - needs: get_config_values - container: - image: ${{ needs.get_config_values.outputs.pinned_image }} - options: --user 1001:1001 --group-add 128 - defaults: - run: - shell: bash - steps: - - name: copy .tool-versions - run: | - cp /home/vscode/.tool-versions "$HOME/.tool-versions" - ... other steps .... -``` -It is important that: -- there is `options: --user 1001:1001 --group-add 128` below image to ensure it uses the correct user id and is added to the docker group -- the default shell is set to be bash -- the first step copies .tool-versions from /home/vscode to $HOME/.tool-versions -## Using local or pull request images in Visual Studio Code and GitHub Actions -You can use local or pull request images by changing IMAGE_VERSION in devcontainer.json. -For an image built locally following instructions below, you should put the IMAGE_VERSION=local-build. -For an image built from a pull request, you should put the IMAGE_VERSION=. -You can only use images built from a pull request for testing changes in GitHub Actions. - -# Common Makefile targets -There is a set of common Makefiles that are defined in `src/base/.devcontainer/Mk` and are included from `common.mk`. These are installed to /usr/local/share/eps/Mk on the base image, so they are available for all containers. - -This should be added to the end of each project's Makefile to include them -``` -%: - @$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@ -``` -# Targets -The following targets are defined. These are needed for quality checks to run. Some targets are project-specific and should be overridden in the project's Makefile. - -Build targets (`build.mk`) -- `install` - placeholder target - should be overridden locally -- `install-node` - placeholder target - should be overridden locally -- `docker-build` - placeholder target - should be overridden locally -- `compile` - placeholder target - should be overridden locally - -Check targets (`check.mk`) -- `lint` - placeholder target - should be overridden locally -- `test` - placeholder target - should be overridden locally -- `shellcheck` - runs shellcheck on `scripts/*.sh` and `.github/scripts/*.sh` when files exist -- `cfn-lint` - runs `cfn-lint` against `cloudformation/**/*.yml|yaml` and `SAMtemplates/**/*.yml|yaml` -- `cdk-synth` - placeholder target - should be overridden locally -- `cfn-guard-sam-templates` - validates SAM templates against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` -- `cfn-guard-cloudformation` - validates `cloudformation` templates against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` -- `cfn-guard-cdk` - validates `cdk.out` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` -- `cfn-guard-terraform` - validates `terraform_plans` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` -- `actionlint` - runs actionlint against GitHub Actions -- `secret-scan` - runs git-secrets (including scanning history) against the repository -- `guard-` - checks if an environment variable is set and errors if it is not - -Credentials targets (`credentials.mk`) -- `aws-configure` - configures an AWS SSO session -- `aws-login` - Authorizes an SSO session with AWS so AWS CLI tools can be used. You may still need to set AWS_PROFILE before running commands -- `github-login` - Authorizes GitHub CLI to GitHub with scope to read packages -- `create-npmrc` - depends on `github-login`, then writes `.npmrc` with a GitHub Packages auth token and `@nhsdigital` registry - -Trivy targets (`trivy.mk`) -- `trivy-license-check` - runs Trivy license scan (HIGH/CRITICAL) and writes `.trivy_out/license_scan.txt` -- `trivy-generate-sbom` - generates CycloneDX SBOM at `.trivy_out/sbom.cdx.json` -- `trivy-scan-python` - scans Python dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_python.txt` -- `trivy-scan-node` - scans Node dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_node.txt` -- `trivy-scan-go` - scans Go dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_go.txt` -- `trivy-scan-java` - scans Java dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_java.txt` -- `trivy-scan-docker` - scans a built image (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_docker.txt` (requires `DOCKER_IMAGE`), for example: - -# Project structure -We have 5 types of dev container. These are defined under src - -`base` - this is the base image that all others are based on. -`base_node` - images that install node - most language projects rely on one of these -`languages` - this installs specific versions of python - normally based off a node image -`projects` - this is used for projects where more customization is needed than just a base language image. -`githubactions` - this just takes an existing image and remaps vscode user to be 1001 so it can be used by GitHub Actions. - -Each image to be built contains a .devcontainer folder that defines how the devcontainer should be built. At a minimum, this should contain a devcontainer.json file. See https://containers.dev/implementors/json_reference/ for options for this - -Images under languages should point to a Dockerfile under src/common or src/common_node_24 that is based off the base or node image. This also runs `.devcontainer/scripts/root_install.sh` and `.devcontainer/scripts/vscode_install.sh` as vscode user as part of the build. These files should be in the language specific folder. - -We use Trivy to scan for vulnerabilities in the built Docker images. Known vulnerabilities in the base image are in `src/common/.trivyignore.yaml`. Vulnerabilities in specific images are in `.trivyignore.yaml` files in each image folder. These are combined before running a scan to exclude all known vulnerabilities - -# Pull requests and merge to main process -For each pull request, and merge to main, images are built and scanned using Trivy, and pushed to GitHub Container Registry. -Docker images are built for AMD64 and ARM64 architecture, and a combined manifest is created and pushed as part of the build. -The main images have a vscode user with ID 1000. A separately tagged image is also created with the vscode user mapped to user ID 1001 so it can be used by GitHub Actions. - -The base image is built first, and then language images, and finally project images. - -Docker images are scanned for vulnerabilities using Trivy as part of a build step, and the build fails if vulnerabilities are found that are not in the .trivyignore file. - -For pull requests, images are tagged with the pr-{pull request id}-{short commit sha}. -For merges to main, images are tagged with the ci-{short commit sha}. -GitHub Actions images are tagged with githubactions-{full tag} -AMD64 images are tagged with {tag}-amd64 -ARM64 images are tagged with {tag}-arm64 -The combined image manifest is tagged with {tag}, so it can be included in devcontainer.json and the correct image is pulled based on the host architecture. - -When a pull request is merged to main or closed, all associated images are deleted from the registry using the GitHub workflow delete_old_images - -# Release workflow -There is a release workflow that runs weekly at 18:00 on Thursday and on demand. -This creates a new release tag, builds all images, and pushes them to GitHub Container Registry. -Images are tagged with the release tag, and also with latest - -# Local testing -## Building images -You can use these commands to build images - -Base image -``` -CONTAINER_NAME=base \ - BASE_VERSION_TAG=latest \ - BASE_FOLDER=. \ - IMAGE_TAG=local-build \ - make build-image -``` -Base node 24 image -``` -CONTAINER_NAME=node_24 \ - BASE_VERSION_TAG=local-build \ - BASE_FOLDER=base_node \ - IMAGE_TAG=local-build \ - make build-image -``` -Language images -``` -CONTAINER_NAME=node_24_python_3_14 \ - BASE_VERSION_TAG=local-build \ - BASE_FOLDER=languages \ - IMAGE_TAG=local-build \ - make build-image -``` -Project images -``` -CONTAINER_NAME=fhir_facade_api \ - BASE_VERSION_TAG=local-build \ - BASE_FOLDER=projects \ - IMAGE_TAG=local-build \ - make build-image -``` -GitHub Actions image -``` -BASE_IMAGE_NAME=base \ - BASE_IMAGE_TAG=local-build \ - IMAGE_TAG=local-build \ - make build-githubactions-image -``` -## Scanning images -You can use these commands to scan images -Base image -``` -CONTAINER_NAME=base \ - BASE_FOLDER=. \ - IMAGE_TAG=local-build \ - make scan-image -``` -Base node 24 image -``` -CONTAINER_NAME=node_24 \ - BASE_FOLDER=base_node \ - IMAGE_TAG=local-build \ - EXTRA_COMMON=common_node_24 \ - make scan-image -``` -Language images -``` -CONTAINER_NAME=node_24_python_3_14 \ - BASE_FOLDER=languages \ - IMAGE_TAG=local-build \ - EXTRA_COMMON=common_node_24 \ - make scan-image -``` -Project images -``` -CONTAINER_NAME=fhir_facade_api \ - BASE_FOLDER=projects \ - IMAGE_TAG=local-build \ - make scan-image -``` - -## Interactive shell on image -You can use this to start an interactive shell in built images -base image -``` -CONTAINER_NAME=base \ - IMAGE_TAG=local-build \ - make shell-image -``` -Language images -``` -CONTAINER_NAME=node_24_python_3_12 \ - IMAGE_TAG=local-build \ - make shell-image -``` -Project images -``` -CONTAINER_NAME=fhir_facade_api \ - IMAGE_TAG=local-build \ - make shell-image -``` -GitHub Actions image -``` -CONTAINER_NAME=base \ - IMAGE_TAG=githubactions-local-build \ - make shell-image -``` +Trivy ([pronunciation][pronunciation]) is a comprehensive and versatile security scanner. +Trivy has *scanners* that look for security issues, and *targets* where it can find those issues. -# Generating a .trivyignore file -You can generate a .trivyignore file for known vulnerabilities by either downloading the JSON scan output generated by the build, or by generating it locally using the scanning images commands above with a make target of scan-image-json +Targets (what Trivy can scan): -If generated locally, then the output goes into .out/scan_results_docker.json. -You can use GitHub CLI tools to download the scan output file. Replace the run ID from the URL, and the -n with the filename to download -``` -gh run download -n scan_results_docker_fhir_facade_api_arm64.json -``` +- Container Image +- Filesystem +- Git Repository (remote) +- Virtual Machine Image +- Kubernetes -Once you have the scan output, use the following to generate a new .trivyignore file called .trivyignore.new.yaml. Note this will overwrite the output file when run so it should point to a new file and the contents merged with existing .trivyignore file +Scanners (what Trivy can find there): +- OS packages and software dependencies in use (SBOM) +- Known vulnerabilities (CVEs) +- IaC issues and misconfigurations +- Sensitive information and secrets +- Software licenses +Trivy supports most popular programming languages, operating systems, and platforms. For a complete list, see the [Scanning Coverage] page. + +To learn more, go to the [Trivy homepage][homepage] for feature highlights, or to the [Documentation site][docs] for detailed information. + +## Quick Start + +### Get Trivy + +Trivy is available in most common distribution channels. The full list of installation options is available in the [Installation] page. Here are a few popular examples: + +- `brew install trivy` +- `docker run aquasec/trivy` +- Download binary from +- See [Installation] for more + +Trivy is integrated with many popular platforms and applications. The complete list of integrations is available in the [Ecosystem] page. Here are a few popular examples: + +- [GitHub Actions](https://github.com/aquasecurity/trivy-action) +- [Kubernetes operator](https://github.com/aquasecurity/trivy-operator) +- [VS Code plugin](https://github.com/aquasecurity/trivy-vscode-extension) +- See [Ecosystem] for more + +### Canary builds +There are canary builds ([Docker Hub](https://hub.docker.com/r/aquasec/trivy/tags?page=1&name=canary), [GitHub](https://github.com/aquasecurity/trivy/pkgs/container/trivy/75776514?tag=canary), [ECR](https://gallery.ecr.aws/aquasecurity/trivy#canary) images and [binaries](https://github.com/aquasecurity/trivy/actions/workflows/canary.yaml)) generated with every push to the main branch. + +Please be aware: canary builds might have critical bugs, so they are not recommended for use in production. + +### General usage + +```bash +trivy [--scanners ] ``` -poetry run python \ - scripts/trivy_to_trivyignore.py \ - --input .out/scan_results_docker.json \ - --output src/projects/fhir_facade_api/.trivyignore.new.yaml + +Examples: + +```bash +trivy image python:3.4-alpine ``` -# Cleaning up unused container images +
+Result + +https://user-images.githubusercontent.com/1161307/171013513-95f18734-233d-45d3-aaf5-d6aec687db0e.mov -There is a script to delete unused container images. This runs on every merge to main and deletes pull request images, and on a weekly schedule it deletes images created by CI. -You can run it manually using the following. Using the `dry-run` flag just shows what would be deleted +
+```bash +trivy fs --scanners vuln,secret,misconfig myproject/ ``` -make github-login -# or gh auth login --scopes read:packages,delete:packages if you want to be able to delete images -bash .github/scripts/delete_unused_images.sh --delete-pr --dry-run -bash .github/scripts/delete_unused_images.sh --delete-ci --dry-run -bash .github/scripts/delete_unused_images.sh --delete-pr --delete-ci + +
+Result + +https://user-images.githubusercontent.com/1161307/171013917-b1f37810-f434-465c-b01a-22de036bd9b3.mov + +
+ +```bash +trivy k8s --report summary cluster ``` -Flags: -- `--dry-run` (`-n`) shows what would be deleted without deleting anything. -- `--delete-pr` deletes images tagged with `pr-...` or `githubactions-pr-...` only when the PR is closed. -- `--delete-ci` deletes images tagged with `ci-<8 hex sha>...` or `githubactions-ci-<8 hex sha>...`. +
+Result + +![k8s summary](docs/imgs/trivy-k8s.png) + +
+ +## FAQ + +### How to pronounce the name "Trivy"? + +`tri` is pronounced like **tri**gger, `vy` is pronounced like en**vy**. + +## Want more? Check out Aqua + +If you liked Trivy, you will love Aqua which builds on top of Trivy to provide even more enhanced capabilities for a complete security management offering. +You can find a high level comparison table specific to Trivy users [here](https://trivy.dev/docs/latest/commercial/compare/). +In addition check out the website for more information about our products and services. +If you'd like to contact Aqua or request a demo, please use this form: + +## Community + +Trivy is an [Aqua Security][aquasec] open source project. +Learn about our open source work and portfolio [here][oss]. +Contact us about any matter by opening a GitHub Discussion [here][discussions] + +Please ensure to abide by our [Code of Conduct][code-of-conduct] during all interactions. + +[test]: https://github.com/aquasecurity/trivy/actions/workflows/test.yaml +[test-img]: https://github.com/aquasecurity/trivy/actions/workflows/test.yaml/badge.svg +[go-report]: https://goreportcard.com/report/github.com/aquasecurity/trivy +[go-report-img]: https://goreportcard.com/badge/github.com/aquasecurity/trivy +[release]: https://github.com/aquasecurity/trivy/releases +[release-img]: https://img.shields.io/github/release/aquasecurity/trivy.svg?logo=github +[github-downloads-img]: https://img.shields.io/github/downloads/aquasecurity/trivy/total?logo=github +[docker-pulls]: https://img.shields.io/docker/pulls/aquasec/trivy?logo=docker&label=docker%20pulls%20%2F%20trivy +[license]: https://github.com/aquasecurity/trivy/blob/main/LICENSE +[license-img]: https://img.shields.io/badge/License-Apache%202.0-blue.svg +[homepage]: https://trivy.dev +[docs]: https://trivy.dev/docs/latest/ +[pronunciation]: #how-to-pronounce-the-name-trivy +[code-of-conduct]: https://github.com/aquasecurity/community/blob/main/CODE_OF_CONDUCT.md + +[Installation]:https://trivy.dev/docs/latest/getting-started/installation/ +[Ecosystem]: https://trivy.dev/docs/latest/ecosystem/ +[Scanning Coverage]: https://trivy.dev/docs/latest/coverage/ + +[alpine]: https://ariadne.space/2021/06/08/the-vulnerability-remediation-lifecycle-of-alpine-containers/ +[rego]: https://www.openpolicyagent.org/docs/latest/#rego +[sigstore]: https://www.sigstore.dev/ -If neither `--delete-pr` nor `--delete-ci` is set, the script defaults to `--delete-pr`. +[aquasec]: https://aquasec.com +[oss]: https://www.aquasec.com/products/open-source-projects/ +[discussions]: https://github.com/aquasecurity/trivy/discussions diff --git a/contrib/asff.tpl b/contrib/asff.tpl new file mode 100644 index 0000000..3cadbb0 --- /dev/null +++ b/contrib/asff.tpl @@ -0,0 +1,161 @@ +{ + "Findings": [ + {{- $t_first := true -}} + {{- range . -}} + {{- $target := .Target -}} + {{- $image := .Target -}} + {{- if gt (len $image) 127 -}} + {{- $image = $image | regexFind ".{124}$" | printf "...%v" -}} + {{- end}} + {{- range .Vulnerabilities -}} + {{- if $t_first -}} + {{- $t_first = false -}} + {{- else -}} + , + {{- end -}} + {{- $severity := .Severity -}} + {{- if eq $severity "UNKNOWN" -}} + {{- $severity = "INFORMATIONAL" -}} + {{- end -}} + {{- $description := .Description -}} + {{- if gt (len $description ) 512 -}} + {{- $description = (substr 0 512 $description) | printf "%v .." -}} + {{- end}} + { + "SchemaVersion": "2018-10-08", + "Id": "{{ $target }}/{{ .VulnerabilityID }}", + "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", + "GeneratorId": "Trivy/{{ .VulnerabilityID }}", + "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", + "Types": [ "Software and Configuration Checks/Vulnerabilities/CVE" ], + "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "Severity": { + "Label": "{{ $severity }}" + }, + "Title": "Trivy found a vulnerability to {{ .VulnerabilityID }} in container {{ $target }}, related to {{ .PkgName }}", + "Description": {{ escapeString $description | printf "%q" }}, + {{ if not (empty .PrimaryURL) -}} + "Remediation": { + "Recommendation": { + "Text": "More information on this vulnerability is provided in the hyperlink", + "Url": "{{ .PrimaryURL }}" + } + }, + {{ end -}} + "ProductFields": { "Product Name": "Trivy" }, + "Resources": [ + { + "Type": "Container", + "Id": "{{ $target }}", + "Partition": "aws", + "Region": "{{ env "AWS_REGION" }}", + "Details": { + "Container": { "ImageName": "{{ $image }}" }, + "Other": { + "CVE ID": "{{ .VulnerabilityID }}", + "CVE Title": {{ .Title | printf "%q" }}, + "PkgName": "{{ .PkgName }}", + "Installed Package": "{{ .InstalledVersion }}", + "Patched Package": "{{ .FixedVersion }}", + "NvdCvssScoreV3": "{{ (index .CVSS (sourceID "nvd")).V3Score }}", + "NvdCvssVectorV3": "{{ (index .CVSS (sourceID "nvd")).V3Vector }}", + "NvdCvssScoreV2": "{{ (index .CVSS (sourceID "nvd")).V2Score }}", + "NvdCvssVectorV2": "{{ (index .CVSS (sourceID "nvd")).V2Vector }}" + } + } + } + ], + "RecordState": "ACTIVE" + } + {{- end -}} + {{- range .Misconfigurations -}} + {{- if $t_first -}}{{- $t_first = false -}}{{- else -}},{{- end -}} + {{- $severity := .Severity -}} + {{- if eq $severity "UNKNOWN" -}} + {{- $severity = "INFORMATIONAL" -}} + {{- end -}} + {{- $description := .Description -}} + {{- if gt (len $description ) 512 -}} + {{- $description = (substr 0 512 $description) | printf "%v .." -}} + {{- end}} + { + "SchemaVersion": "2018-10-08", + "Id": "{{ $target }}/{{ .ID }}", + "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", + "GeneratorId": "Trivy/{{ .ID }}", + "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", + "Types": [ "Software and Configuration Checks" ], + "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "Severity": { + "Label": "{{ $severity }}" + }, + "Title": "Trivy found a misconfiguration in {{ $target }}: {{ escapeString .Title }}", + "Description": {{ escapeString $description | printf "%q" }}, + "Remediation": { + "Recommendation": { + "Text": "{{ .Resolution }}", + "Url": "{{ .PrimaryURL }}" + } + }, + "ProductFields": { "Product Name": "Trivy" }, + "Resources": [ + { + "Type": "Other", + "Id": "{{ $target }}", + "Partition": "aws", + "Region": "{{ env "AWS_REGION" }}", + "Details": { + "Other": { + "Message": "{{ escapeString .Message }}", + "Filename": "{{ $target }}", + "StartLine": "{{ .CauseMetadata.StartLine }}", + "EndLine": "{{ .CauseMetadata.EndLine }}" + } + } + } + ], + "RecordState": "ACTIVE" + } + {{- end -}} + {{- range .Secrets -}} + {{- if $t_first -}}{{- $t_first = false -}}{{- else -}},{{- end -}} + {{- $severity := .Severity -}} + {{- if eq $severity "UNKNOWN" -}} + {{- $severity = "INFORMATIONAL" -}} + {{- end -}} + { + "SchemaVersion": "2018-10-08", + "Id": "{{ $target }}", + "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", + "GeneratorId": "Trivy", + "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", + "Types": [ "Sensitive Data Identifications" ], + "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", + "Severity": { + "Label": "{{ $severity }}" + }, + "Title": "Trivy found a secret in {{ $target }}: {{ .Title }}", + "Description": "Trivy found a secret in {{ $target }}: {{ .Title }}", + "ProductFields": { "Product Name": "Trivy" }, + "Resources": [ + { + "Type": "Other", + "Id": "{{ $target }}", + "Partition": "aws", + "Region": "{{ env "AWS_REGION" }}", + "Details": { + "Other": { + "Filename": "{{ $target }}" + } + } + } + ], + "RecordState": "ACTIVE" + } + {{- end -}} + {{- end }} + ] +} diff --git a/contrib/gitlab-codequality.tpl b/contrib/gitlab-codequality.tpl new file mode 100644 index 0000000..5beec25 --- /dev/null +++ b/contrib/gitlab-codequality.tpl @@ -0,0 +1,103 @@ +{{- /* Template based on https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#data-types */ -}} +[ + {{- $t_first := true }} + {{- range . }} + {{- $target := .Target }} + {{- range .Vulnerabilities -}} + {{- if $t_first -}} + {{- $t_first = false -}} + {{ else -}} + , + {{- end }} + { + "type": "issue", + "check_name": "container_scanning", + "categories": [ "Security" ], + "description": {{ list .VulnerabilityID .PkgName .InstalledVersion .Title | join " - " | printf "%q" }}, + "fingerprint": "{{ list .VulnerabilityID .PkgName .InstalledVersion $target | join "" | sha1sum }}", + "content": {{ .Description | printf "%q" }}, + "severity": {{ if eq .Severity "LOW" -}} + "info" + {{- else if eq .Severity "MEDIUM" -}} + "minor" + {{- else if eq .Severity "HIGH" -}} + "major" + {{- else if eq .Severity "CRITICAL" -}} + "critical" + {{- else -}} + "info" + {{- end }}, + "location": { + "path": "{{ $target }}", + "lines": { + "begin": 0 + } + } + } + {{- end -}} + {{- range .Misconfigurations -}} + {{- if $t_first -}} + {{- $t_first = false -}} + {{ else -}} + , + {{- end }} + { + "type": "issue", + "check_name": "container_scanning", + "categories": [ "Security" ], + "description": {{ list "Misconfig" .ID .Title | join " - " | printf "%q" }}, + "fingerprint": "{{ list .ID .Title $target | join "" | sha1sum }}", + "content": {{ .Description | printf "%q" }}, + "severity": {{ if eq .Severity "LOW" -}} + "info" + {{- else if eq .Severity "MEDIUM" -}} + "minor" + {{- else if eq .Severity "HIGH" -}} + "major" + {{- else if eq .Severity "CRITICAL" -}} + "critical" + {{- else -}} + "info" + {{- end }}, + "location": { + "path": "{{ $target }}", + "lines": { + "begin": {{ .CauseMetadata.StartLine }} + } + } + } + {{- end -}} + {{- range .Secrets -}} + {{- if $t_first -}} + {{- $t_first = false -}} + {{ else -}} + , + {{- end }} + { + "type": "issue", + "check_name": "container_scanning", + "categories": [ "Security" ], + "description": {{ list "Secret" .RuleID .Title | join " - " | printf "%q" }}, + "fingerprint": "{{ list .RuleID .Title $target | join "" | sha1sum }}", + "content": {{ .Title | printf "%q" }}, + "severity": {{ if eq .Severity "LOW" -}} + "info" + {{- else if eq .Severity "MEDIUM" -}} + "minor" + {{- else if eq .Severity "HIGH" -}} + "major" + {{- else if eq .Severity "CRITICAL" -}} + "critical" + {{- else -}} + "info" + {{- end }}, + "location": { + "path": "{{ $target }}", + "lines": { + "begin": {{ .StartLine }} + } + } + } + {{- end -}} + {{- end }} +] diff --git a/contrib/gitlab.tpl b/contrib/gitlab.tpl new file mode 100644 index 0000000..7ff8abd --- /dev/null +++ b/contrib/gitlab.tpl @@ -0,0 +1,112 @@ +{{- /* Template based on https://docs.gitlab.com/ee/user/application_security/container_scanning/#reports-json-format */ -}} +{ + "version": "15.0.7", + "scan": { + "analyzer": { + "id": "trivy", + "name": "Trivy", + "vendor": { + "name": "Aqua Security" + }, + "version": "{{ appVersion }}" + }, + "end_time": "{{ now | date "2006-01-02T15:04:05" }}", + "scanner": { + "id": "trivy", + "name": "Trivy", + "url": "https://github.com/aquasecurity/trivy/", + "vendor": { + "name": "Aqua Security" + }, + "version": "{{ appVersion }}" + }, + "start_time": "{{ now | date "2006-01-02T15:04:05" }}", + "status": "success", + "type": "container_scanning" + }, + {{- $image := "Unknown" -}} + {{- $os := "Unknown" -}} + {{- range . }} + {{- if eq .Class "os-pkgs" -}} + {{- $target := .Target }} + {{- $image = $target | regexFind "[^\\s]+" }} + {{- $os = $target | splitList "(" | last | trimSuffix ")" }} + {{- end }} + {{- end }} + "vulnerabilities": [ + {{- $t_first := true }} + {{- range . }} + {{- range .Vulnerabilities -}} + {{- if $t_first -}} + {{- $t_first = false -}} + {{ else -}} + , + {{- end }} + { + "id": "{{ .VulnerabilityID }}", + "name": {{ .Title | printf "%q" }}, + "description": {{ .Description | printf "%q" }}, + "severity": {{ if eq .Severity "UNKNOWN" -}} + "Unknown" + {{- else if eq .Severity "LOW" -}} + "Low" + {{- else if eq .Severity "MEDIUM" -}} + "Medium" + {{- else if eq .Severity "HIGH" -}} + "High" + {{- else if eq .Severity "CRITICAL" -}} + "Critical" + {{- else -}} + "{{ .Severity }}" + {{- end }}, + "solution": {{ if .FixedVersion -}} + "Upgrade {{ .PkgName }} to {{ .FixedVersion }}" + {{- else -}} + "No solution provided" + {{- end }}, + "location": { + "dependency": { + "package": { + "name": "{{ .PkgName }}" + }, + "version": "{{ .InstalledVersion }}" + }, + {{- /* TODO: No mapping available - https://github.com/aquasecurity/trivy/issues/332 */}} + "operating_system": "{{ $os }}", + "image": "{{ $image }}" + }, + "identifiers": [ + { + {{- /* TODO: Type not extractable - https://github.com/aquasecurity/trivy-db/pull/24 */}} + "type": "cve", + "name": "{{ .VulnerabilityID }}", + "value": "{{ .VulnerabilityID }}" + {{- /* cf. https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/e3d280d7f0862ca66a1555ea8b24016a004bb914/dist/container-scanning-report-format.json#L157-179 */}} + {{- if .PrimaryURL | regexMatch "^(https?|ftp)://.+" -}}, + "url": "{{ .PrimaryURL }}" + {{- end }} + } + ], + "links": [ + {{- $l_first := true -}} + {{- range .References -}} + {{- if $l_first -}} + {{- $l_first = false }} + {{- else -}} + , + {{- end -}} + {{- if . | regexMatch "^(https?|ftp)://.+" -}} + { + "url": "{{ . }}" + } + {{- else -}} + {{- $l_first = true }} + {{- end -}} + {{- end }} + ] + } + {{- end -}} + {{- end }} + ], + "remediations": [] +} diff --git a/contrib/html.tpl b/contrib/html.tpl new file mode 100644 index 0000000..e92b1b1 --- /dev/null +++ b/contrib/html.tpl @@ -0,0 +1,148 @@ + + + + +{{- if . }} + + {{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }} + + + +

{{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }}

+ + {{- range . }} + + {{- if (eq (len .Vulnerabilities) 0) }} + + {{- else }} + + + + + + + + + {{- range .Vulnerabilities }} + + + + + + + + + {{- end }} + {{- end }} + {{- if (eq (len .Misconfigurations ) 0) }} + + {{- else }} + + + + + + + + {{- range .Misconfigurations }} + + + + + + + + {{- end }} + {{- end }} + {{- end }} +
{{ .Type | toString | escapeXML }}
No Vulnerabilities found
PackageVulnerability IDSeverityInstalled VersionFixed VersionLinks
{{ escapeXML .PkgName }}{{ escapeXML .VulnerabilityID }}{{ escapeXML .Vulnerability.Severity }}{{ escapeXML .InstalledVersion }}{{ escapeXML .FixedVersion }}
No Misconfigurations found
TypeMisconf IDCheckSeverityMessage
{{ escapeXML .Type }}{{ escapeXML .ID }}{{ escapeXML .Title }}{{ escapeXML .Severity }}
+{{- else }} + + +

Trivy Returned Empty Report

+{{- end }} + + diff --git a/contrib/junit.tpl b/contrib/junit.tpl new file mode 100644 index 0000000..e4313c1 --- /dev/null +++ b/contrib/junit.tpl @@ -0,0 +1,75 @@ + + +{{- range . -}} +{{- $failures := len .Vulnerabilities }} + + {{- if not (eq .Type "") }} + + + + {{- end -}} + {{ range .Vulnerabilities }} + + {{ escapeXML .Description }} + + {{- end }} + + +{{- $target := .Target }} +{{- if .MisconfSummary }} + +{{- else }} + +{{- end }} + {{- if not (eq .Type "") }} + + + + {{- end -}} + {{ range .Misconfigurations }} + + {{- if (eq .Status "FAIL") }} + + {{- $target }}: + {{- with .CauseMetadata }} + {{- .StartLine }} + {{- if lt .StartLine .EndLine }}:{{ .EndLine }}{{ end }}: Occurrences: + {{- range $i := .Occurrences -}} + via {{ .Filename }}: + {{- .Location.StartLine }} + {{- if lt .Location.StartLine .Location.EndLine }}:{{ .Location.EndLine }}{{ end }} ({{ .Resource }}) + {{- end -}} + Code: + {{- range .Code.Lines }} + {{- if .IsCause }}{{ escapeXML .Content }} {{- end }} + {{- end }} + {{- end }} + {{- escapeXML .Description }} + + {{- end }} + + {{- end }} + + +{{- if .Licenses }} + {{- $licenses := len .Licenses }} + {{ range .Licenses }} + + + + {{- end }} + +{{- end }} + +{{- if .Secrets }} + {{- $secrets := len .Secrets }} + {{ range .Secrets }} + + {{ escapeXML .Match }} + + {{- end }} + +{{- end }} + +{{- end }} + diff --git a/scripts/install_cosign.sh b/scripts/install_cosign.sh index f5ae297..6cb9174 100755 --- a/scripts/install_cosign.sh +++ b/scripts/install_cosign.sh @@ -3,7 +3,6 @@ set -euo pipefail DEFAULT_INSTALL_DIR="/usr/local/bin" INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" -mkdir -p "$INSTALL_DIR" REQUESTED_VERSION="${1:-latest}" OS="$(uname -s)" ARCH="$(uname -m)" @@ -65,15 +64,19 @@ BASE_URL="https://github.com/sigstore/cosign/releases/download/${VERSION}" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT +download() { + local url="${1}" dest="${2}" + echo "Downloading ${dest} ..." + curl -fsSL "${url}" -o "${dest}" +} + BIN_PATH="$TMP_DIR/${BINARY_NAME}" SIGSTORE_PATH="$TMP_DIR/${BINARY_NAME}-kms.sigstore.json" ARTIFACT_PATH="$TMP_DIR/artifact.pub" DECODED_SIGSTORE_PATH="$TMP_DIR/cosign-kms.sig.decoded" -echo "downloading ${BINARY_NAME} version ${VERSION} from ${BASE_URL}" -curl -fsSL "${BASE_URL}/${BINARY_NAME}" -o "$BIN_PATH" -echo "downloading sigstore signature" -curl -fsSL "${BASE_URL}/${BINARY_NAME}-kms.sigstore.json" -o "$SIGSTORE_PATH" +download "${BASE_URL}/${BINARY_NAME}" "$BIN_PATH" +download "${BASE_URL}/${BINARY_NAME}-kms.sigstore.json" "$SIGSTORE_PATH" # install tuf-client go install github.com/theupdateframework/go-tuf/cmd/tuf-client@latest @@ -96,7 +99,7 @@ echo "verifying signature with cosign verify-blob" chmod +x "$BIN_PATH" ${BIN_PATH} verify-blob --bundle "${SIGSTORE_PATH}" --key "$ARTIFACT_PATH" "$BIN_PATH" - +mkdir -p "$INSTALL_DIR" install -m 0755 "$BIN_PATH" "${INSTALL_DIR}/cosign" "${INSTALL_DIR}/cosign" version diff --git a/scripts/download_latest_trivy.sh b/scripts/install_trivy.sh similarity index 64% rename from scripts/download_latest_trivy.sh rename to scripts/install_trivy.sh index 13235ca..b27ee36 100755 --- a/scripts/download_latest_trivy.sh +++ b/scripts/install_trivy.sh @@ -1,18 +1,18 @@ #!/usr/bin/env bash set -euo pipefail +DEFAULT_INSTALL_DIR="/usr/local/bin" +INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" VERSION="v0.69.3" RELEASE_NUMBER="${VERSION#v}" BASE_URL="https://github.com/aquasecurity/trivy/releases/download/${VERSION}" ARCHIVE="trivy_${RELEASE_NUMBER}_Linux-64bit.tar.gz" BUNDLE="${ARCHIVE}.sigstore.json" -CHECKSUM_FILE="${ARCHIVE}.sha256" CERT_IDENTITY="https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/${VERSION}" -OUTPUT_DIR="${1:-$PWD}" usage() { cat <<'EOF' -Usage: download_latest_trivy.sh [output_dir] +Usage: install_trivy.sh [output_dir] Downloads Trivy v0.69.3, its sigstore bundle, and checksum into output_dir (default: current directory), then verifies the checksum and the sigstore bundle, following @@ -32,35 +32,30 @@ for cmd in curl cosign sha256sum; do fi done -mkdir -p "$OUTPUT_DIR" -pushd "$OUTPUT_DIR" >/dev/null -trap 'popd >/dev/null' EXIT +TMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TMP_DIR"' EXIT download() { local url="${1}" dest="${2}" echo "Downloading ${dest} ..." curl -fsSL "${url}" -o "${dest}" } +ARCHIVE_PATH="${TMP_DIR}/${ARCHIVE}" +BUNDLE_PATH="${TMP_DIR}/${BUNDLE}" +download "${BASE_URL}/${ARCHIVE}" "${ARCHIVE_PATH}" +download "${BASE_URL}/${BUNDLE}" "${BUNDLE_PATH}" -set_downloads() { - download "${BASE_URL}/${ARCHIVE}" "${ARCHIVE}" - download "${BASE_URL}/${BUNDLE}" "${BUNDLE}" - download "${BASE_URL}/${CHECKSUM_FILE}" "${CHECKSUM_FILE}" -} - -set_downloads - -if sha256sum -c "${CHECKSUM_FILE}"; then - echo "Checksum verification passed" -else - echo "Checksum verification failed" >&2 - exit 1 -fi -cosign verify-blob-attestation "${ARCHIVE}" \ - --bundle "${BUNDLE}" \ +cosign verify-blob-attestation "${ARCHIVE_PATH}" \ + --bundle "${BUNDLE_PATH}" \ --certificate-oidc-issuer="https://token.actions.githubusercontent.com" \ --certificate-identity "${CERT_IDENTITY}" echo "Sigstore verification passed" -echo "Trivy ${VERSION} download verified in ${OUTPUT_DIR}" +tar -xzf "${ARCHIVE_PATH}" -C "${TMP_DIR}" +mkdir -p "$INSTALL_DIR" +install -m 0755 "$TMP_DIR/trivy" "${INSTALL_DIR}/trivy" + +"${INSTALL_DIR}/trivy" version + +echo "trivy ${VERSION} installed to ${INSTALL_DIR}" From 2b3b24e99b56984ce503b3f0b263b20877d0f4f5 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 17:56:22 +0000 Subject: [PATCH 04/13] verify trivy in build --- .github/workflows/build_all_images.yml | 29 ++++++++++++++++++++ .github/workflows/build_multi_arch_image.yml | 10 +++++-- scripts/install_trivy.sh | 9 ++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 111abf8..a79d9fd 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -33,6 +33,35 @@ jobs: echo "node_24_languages=$node_24_language_folders" echo "projects=$project_folders" } >> "$GITHUB_OUTPUT" + download_trivy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: Install cosign + run: | + ./scripts/install_cosign.sh + env: + INSTALL_DIR: ${HOME}/.local/bin + - name: Get amd64 trivy + run: | + ./scripts/install_trivy.sh + env: + INSTALL_DIR: trivy_amd64 + ARCH: 64bit + - name: Get arm64 trivy + run: | + ./scripts/install_trivy.sh + env: + INSTALL_DIR: trivy_arm64 + ARCH: ARM64 + - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f + name: Upload trivy + with: + name: "trivy" + path: | + trivy_amd64/trivy + trivy_arm64/trivy + package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml with: diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index b9d5334..b2e8000 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -63,10 +63,14 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 - - name: setup trivy - uses: aquasecurity/setup-trivy@3fb12ec12f41e471780db15c232d5dd185dcb514 + - name: Download trivy + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c with: - version: v0.69.3 + name: trivy + - name: setup trivy + run: | + sudo cp "trivy/trivy_${ARCH}/trivy" /usr/local/bin/ + chmod +x /usr/local/bin/trivy - name: setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f with: diff --git a/scripts/install_trivy.sh b/scripts/install_trivy.sh index b27ee36..0de665a 100755 --- a/scripts/install_trivy.sh +++ b/scripts/install_trivy.sh @@ -4,9 +4,13 @@ set -euo pipefail DEFAULT_INSTALL_DIR="/usr/local/bin" INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" VERSION="v0.69.3" +DEFAULT_ARCH="64bit" +ARCH="${ARCH:-$DEFAULT_ARCH}" +#trivy_0.69.3_Linux-64bit.tar.gz +#trivy_0.69.3_Linux-ARM64.tar.gz RELEASE_NUMBER="${VERSION#v}" BASE_URL="https://github.com/aquasecurity/trivy/releases/download/${VERSION}" -ARCHIVE="trivy_${RELEASE_NUMBER}_Linux-64bit.tar.gz" +ARCHIVE="trivy_${RELEASE_NUMBER}_Linux-${ARCH}.tar.gz" BUNDLE="${ARCHIVE}.sigstore.json" CERT_IDENTITY="https://github.com/aquasecurity/trivy/.github/workflows/reusable-release.yaml@refs/tags/${VERSION}" @@ -53,9 +57,8 @@ cosign verify-blob-attestation "${ARCHIVE_PATH}" \ echo "Sigstore verification passed" tar -xzf "${ARCHIVE_PATH}" -C "${TMP_DIR}" + mkdir -p "$INSTALL_DIR" install -m 0755 "$TMP_DIR/trivy" "${INSTALL_DIR}/trivy" -"${INSTALL_DIR}/trivy" version - echo "trivy ${VERSION} installed to ${INSTALL_DIR}" From 1b0e1f21afabc31c5e11e6b7f507a6bdfe189404 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 18:36:03 +0000 Subject: [PATCH 05/13] build it another way --- .devcontainer/Dockerfile | 22 ++-- .devcontainer/Dockerfile.test | 10 ++ .github/workflows/build_all_images.yml | 3 + .gitignore | 1 + .tool-versions | 1 - Makefile | 4 + contrib/asff.tpl | 161 ------------------------- contrib/gitlab-codequality.tpl | 103 ---------------- contrib/gitlab.tpl | 112 ----------------- contrib/html.tpl | 148 ----------------------- contrib/junit.tpl | 75 ------------ scripts/install_cosign.sh | 3 +- 12 files changed, 32 insertions(+), 611 deletions(-) create mode 100644 .devcontainer/Dockerfile.test delete mode 100644 contrib/asff.tpl delete mode 100644 contrib/gitlab-codequality.tpl delete mode 100644 contrib/gitlab.tpl delete mode 100644 contrib/html.tpl delete mode 100644 contrib/junit.tpl diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index e047cfe..177b6d8 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,3 +1,14 @@ +FROM golang:1.26.1-bookworm AS build +RUN apt-get update && apt-get install -y \ + jq \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY scripts/install_cosign.sh /tmp/install_cosign.sh +COPY scripts/install_trivy.sh /tmp/install_trivy.sh +RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh +RUN INSTALL_DIR=/tmp/trivy_arm64 ARCH=ARM64 /tmp/install_trivy.sh +RUN INSTALL_DIR=/tmp/trivy_amd64 ARCH=64bit /tmp/install_trivy.sh + FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 ARG TARGETARCH ENV TARGETARCH=${TARGETARCH} @@ -63,7 +74,7 @@ RUN git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets && \ mkdir -p /usr/share/secrets-scanner && \ chmod 755 /usr/share/secrets-scanner && \ curl -L https://raw.githubusercontent.com/NHSDigital/software-engineering-quality-framework/main/tools/nhsd-git-secrets/nhsd-rules-deny.txt -o /usr/share/secrets-scanner/nhsd-rules-deny.txt - +COPY --from=build /tmp/trivy_amd64/trivy /usr/local/bin/trivy USER vscode ENV PATH="/home/vscode/.asdf/shims:/home/vscode/.local/bin:$PATH:/workspaces/eps-devcontainers/node_modules/.bin" @@ -83,9 +94,7 @@ RUN asdf plugin add python; \ asdf plugin add actionlint; \ asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git; \ asdf plugin add trivy https://github.com/zufardhiyaulhaq/asdf-trivy.git; \ - asdf plugin add yq https://github.com/sudermanjr/asdf-yq.git; \ - asdf plugin add golang - + asdf plugin add yq https://github.com/sudermanjr/asdf-yq.git; WORKDIR /workspaces/eps-devcontainers COPY .tool-versions /workspaces/eps-devcontainers/.tool-versions @@ -95,10 +104,5 @@ COPY .tool-versions /home/vscode/.tool-versions RUN asdf install python; \ asdf install -COPY scripts/install_cosign.sh /tmp/install_cosign.sh -COPY scripts/install_trivy.sh /tmp/install_trivy.sh -RUN INSTALL_DIR=/home/vscode/.local/bin /tmp/install_cosign.sh -RUN INSTALL_DIR=/home/vscode/.local/bin /tmp/install_trivy.sh - RUN git-secrets --register-aws --global && \ git-secrets --add-provider --global -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt diff --git a/.devcontainer/Dockerfile.test b/.devcontainer/Dockerfile.test new file mode 100644 index 0000000..c021539 --- /dev/null +++ b/.devcontainer/Dockerfile.test @@ -0,0 +1,10 @@ +FROM golang:1.26.1-bookworm +RUN apt-get update && apt-get install -y \ + jq \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY scripts/install_cosign.sh /tmp/install_cosign.sh +COPY scripts/install_trivy.sh /tmp/install_trivy.sh +RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh +RUN INSTALL_DIR=trivy_arm64 ARCH=ARM64 /tmp/install_trivy.sh +RUN INSTALL_DIR=trivy_amd64 ARCH=64bit /tmp/install_trivy.sh diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index a79d9fd..2f3db5b 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -64,6 +64,9 @@ jobs: package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml + needs: [ + download_trivy + ] with: tag_latest: ${{ inputs.tag_latest }} docker_tag: ${{ inputs.docker_tag }} diff --git a/.gitignore b/.gitignore index 35bc1fd..7c362b6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ src/base/.devcontainer/language_versions/ .trivyignore_combined.yaml .out/ .envrc +.trivy_out/ diff --git a/.tool-versions b/.tool-versions index 5cd8921..2500101 100644 --- a/.tool-versions +++ b/.tool-versions @@ -6,4 +6,3 @@ direnv 2.37.1 actionlint 1.7.10 ruby 3.3.0 yq 4.52.2 -golang 1.24.13 diff --git a/Makefile b/Makefile index 8b40ad8..3bb8ed9 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,8 @@ CONTAINER_PREFIX=ghcr.io/nhsdigital/eps-devcontainers/ +include src/base/.devcontainer/Mk/build.mk +include src/base/.devcontainer/Mk/check.mk +include src/base/.devcontainer/Mk/trivy.mk +include src/base/.devcontainer/Mk/credentials.mk ifeq ($(strip $(NO_CACHE)),true) NO_CACHE_FLAG=--no-cache diff --git a/contrib/asff.tpl b/contrib/asff.tpl deleted file mode 100644 index 3cadbb0..0000000 --- a/contrib/asff.tpl +++ /dev/null @@ -1,161 +0,0 @@ -{ - "Findings": [ - {{- $t_first := true -}} - {{- range . -}} - {{- $target := .Target -}} - {{- $image := .Target -}} - {{- if gt (len $image) 127 -}} - {{- $image = $image | regexFind ".{124}$" | printf "...%v" -}} - {{- end}} - {{- range .Vulnerabilities -}} - {{- if $t_first -}} - {{- $t_first = false -}} - {{- else -}} - , - {{- end -}} - {{- $severity := .Severity -}} - {{- if eq $severity "UNKNOWN" -}} - {{- $severity = "INFORMATIONAL" -}} - {{- end -}} - {{- $description := .Description -}} - {{- if gt (len $description ) 512 -}} - {{- $description = (substr 0 512 $description) | printf "%v .." -}} - {{- end}} - { - "SchemaVersion": "2018-10-08", - "Id": "{{ $target }}/{{ .VulnerabilityID }}", - "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", - "GeneratorId": "Trivy/{{ .VulnerabilityID }}", - "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", - "Types": [ "Software and Configuration Checks/Vulnerabilities/CVE" ], - "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "Severity": { - "Label": "{{ $severity }}" - }, - "Title": "Trivy found a vulnerability to {{ .VulnerabilityID }} in container {{ $target }}, related to {{ .PkgName }}", - "Description": {{ escapeString $description | printf "%q" }}, - {{ if not (empty .PrimaryURL) -}} - "Remediation": { - "Recommendation": { - "Text": "More information on this vulnerability is provided in the hyperlink", - "Url": "{{ .PrimaryURL }}" - } - }, - {{ end -}} - "ProductFields": { "Product Name": "Trivy" }, - "Resources": [ - { - "Type": "Container", - "Id": "{{ $target }}", - "Partition": "aws", - "Region": "{{ env "AWS_REGION" }}", - "Details": { - "Container": { "ImageName": "{{ $image }}" }, - "Other": { - "CVE ID": "{{ .VulnerabilityID }}", - "CVE Title": {{ .Title | printf "%q" }}, - "PkgName": "{{ .PkgName }}", - "Installed Package": "{{ .InstalledVersion }}", - "Patched Package": "{{ .FixedVersion }}", - "NvdCvssScoreV3": "{{ (index .CVSS (sourceID "nvd")).V3Score }}", - "NvdCvssVectorV3": "{{ (index .CVSS (sourceID "nvd")).V3Vector }}", - "NvdCvssScoreV2": "{{ (index .CVSS (sourceID "nvd")).V2Score }}", - "NvdCvssVectorV2": "{{ (index .CVSS (sourceID "nvd")).V2Vector }}" - } - } - } - ], - "RecordState": "ACTIVE" - } - {{- end -}} - {{- range .Misconfigurations -}} - {{- if $t_first -}}{{- $t_first = false -}}{{- else -}},{{- end -}} - {{- $severity := .Severity -}} - {{- if eq $severity "UNKNOWN" -}} - {{- $severity = "INFORMATIONAL" -}} - {{- end -}} - {{- $description := .Description -}} - {{- if gt (len $description ) 512 -}} - {{- $description = (substr 0 512 $description) | printf "%v .." -}} - {{- end}} - { - "SchemaVersion": "2018-10-08", - "Id": "{{ $target }}/{{ .ID }}", - "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", - "GeneratorId": "Trivy/{{ .ID }}", - "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", - "Types": [ "Software and Configuration Checks" ], - "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "Severity": { - "Label": "{{ $severity }}" - }, - "Title": "Trivy found a misconfiguration in {{ $target }}: {{ escapeString .Title }}", - "Description": {{ escapeString $description | printf "%q" }}, - "Remediation": { - "Recommendation": { - "Text": "{{ .Resolution }}", - "Url": "{{ .PrimaryURL }}" - } - }, - "ProductFields": { "Product Name": "Trivy" }, - "Resources": [ - { - "Type": "Other", - "Id": "{{ $target }}", - "Partition": "aws", - "Region": "{{ env "AWS_REGION" }}", - "Details": { - "Other": { - "Message": "{{ escapeString .Message }}", - "Filename": "{{ $target }}", - "StartLine": "{{ .CauseMetadata.StartLine }}", - "EndLine": "{{ .CauseMetadata.EndLine }}" - } - } - } - ], - "RecordState": "ACTIVE" - } - {{- end -}} - {{- range .Secrets -}} - {{- if $t_first -}}{{- $t_first = false -}}{{- else -}},{{- end -}} - {{- $severity := .Severity -}} - {{- if eq $severity "UNKNOWN" -}} - {{- $severity = "INFORMATIONAL" -}} - {{- end -}} - { - "SchemaVersion": "2018-10-08", - "Id": "{{ $target }}", - "ProductArn": "arn:aws:securityhub:{{ env "AWS_REGION" }}::product/aquasecurity/aquasecurity", - "GeneratorId": "Trivy", - "AwsAccountId": "{{ env "AWS_ACCOUNT_ID" }}", - "Types": [ "Sensitive Data Identifications" ], - "CreatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "UpdatedAt": "{{ now | date "2006-01-02T15:04:05.999999999Z07:00" }}", - "Severity": { - "Label": "{{ $severity }}" - }, - "Title": "Trivy found a secret in {{ $target }}: {{ .Title }}", - "Description": "Trivy found a secret in {{ $target }}: {{ .Title }}", - "ProductFields": { "Product Name": "Trivy" }, - "Resources": [ - { - "Type": "Other", - "Id": "{{ $target }}", - "Partition": "aws", - "Region": "{{ env "AWS_REGION" }}", - "Details": { - "Other": { - "Filename": "{{ $target }}" - } - } - } - ], - "RecordState": "ACTIVE" - } - {{- end -}} - {{- end }} - ] -} diff --git a/contrib/gitlab-codequality.tpl b/contrib/gitlab-codequality.tpl deleted file mode 100644 index 5beec25..0000000 --- a/contrib/gitlab-codequality.tpl +++ /dev/null @@ -1,103 +0,0 @@ -{{- /* Template based on https://github.com/codeclimate/platform/blob/master/spec/analyzers/SPEC.md#data-types */ -}} -[ - {{- $t_first := true }} - {{- range . }} - {{- $target := .Target }} - {{- range .Vulnerabilities -}} - {{- if $t_first -}} - {{- $t_first = false -}} - {{ else -}} - , - {{- end }} - { - "type": "issue", - "check_name": "container_scanning", - "categories": [ "Security" ], - "description": {{ list .VulnerabilityID .PkgName .InstalledVersion .Title | join " - " | printf "%q" }}, - "fingerprint": "{{ list .VulnerabilityID .PkgName .InstalledVersion $target | join "" | sha1sum }}", - "content": {{ .Description | printf "%q" }}, - "severity": {{ if eq .Severity "LOW" -}} - "info" - {{- else if eq .Severity "MEDIUM" -}} - "minor" - {{- else if eq .Severity "HIGH" -}} - "major" - {{- else if eq .Severity "CRITICAL" -}} - "critical" - {{- else -}} - "info" - {{- end }}, - "location": { - "path": "{{ $target }}", - "lines": { - "begin": 0 - } - } - } - {{- end -}} - {{- range .Misconfigurations -}} - {{- if $t_first -}} - {{- $t_first = false -}} - {{ else -}} - , - {{- end }} - { - "type": "issue", - "check_name": "container_scanning", - "categories": [ "Security" ], - "description": {{ list "Misconfig" .ID .Title | join " - " | printf "%q" }}, - "fingerprint": "{{ list .ID .Title $target | join "" | sha1sum }}", - "content": {{ .Description | printf "%q" }}, - "severity": {{ if eq .Severity "LOW" -}} - "info" - {{- else if eq .Severity "MEDIUM" -}} - "minor" - {{- else if eq .Severity "HIGH" -}} - "major" - {{- else if eq .Severity "CRITICAL" -}} - "critical" - {{- else -}} - "info" - {{- end }}, - "location": { - "path": "{{ $target }}", - "lines": { - "begin": {{ .CauseMetadata.StartLine }} - } - } - } - {{- end -}} - {{- range .Secrets -}} - {{- if $t_first -}} - {{- $t_first = false -}} - {{ else -}} - , - {{- end }} - { - "type": "issue", - "check_name": "container_scanning", - "categories": [ "Security" ], - "description": {{ list "Secret" .RuleID .Title | join " - " | printf "%q" }}, - "fingerprint": "{{ list .RuleID .Title $target | join "" | sha1sum }}", - "content": {{ .Title | printf "%q" }}, - "severity": {{ if eq .Severity "LOW" -}} - "info" - {{- else if eq .Severity "MEDIUM" -}} - "minor" - {{- else if eq .Severity "HIGH" -}} - "major" - {{- else if eq .Severity "CRITICAL" -}} - "critical" - {{- else -}} - "info" - {{- end }}, - "location": { - "path": "{{ $target }}", - "lines": { - "begin": {{ .StartLine }} - } - } - } - {{- end -}} - {{- end }} -] diff --git a/contrib/gitlab.tpl b/contrib/gitlab.tpl deleted file mode 100644 index 7ff8abd..0000000 --- a/contrib/gitlab.tpl +++ /dev/null @@ -1,112 +0,0 @@ -{{- /* Template based on https://docs.gitlab.com/ee/user/application_security/container_scanning/#reports-json-format */ -}} -{ - "version": "15.0.7", - "scan": { - "analyzer": { - "id": "trivy", - "name": "Trivy", - "vendor": { - "name": "Aqua Security" - }, - "version": "{{ appVersion }}" - }, - "end_time": "{{ now | date "2006-01-02T15:04:05" }}", - "scanner": { - "id": "trivy", - "name": "Trivy", - "url": "https://github.com/aquasecurity/trivy/", - "vendor": { - "name": "Aqua Security" - }, - "version": "{{ appVersion }}" - }, - "start_time": "{{ now | date "2006-01-02T15:04:05" }}", - "status": "success", - "type": "container_scanning" - }, - {{- $image := "Unknown" -}} - {{- $os := "Unknown" -}} - {{- range . }} - {{- if eq .Class "os-pkgs" -}} - {{- $target := .Target }} - {{- $image = $target | regexFind "[^\\s]+" }} - {{- $os = $target | splitList "(" | last | trimSuffix ")" }} - {{- end }} - {{- end }} - "vulnerabilities": [ - {{- $t_first := true }} - {{- range . }} - {{- range .Vulnerabilities -}} - {{- if $t_first -}} - {{- $t_first = false -}} - {{ else -}} - , - {{- end }} - { - "id": "{{ .VulnerabilityID }}", - "name": {{ .Title | printf "%q" }}, - "description": {{ .Description | printf "%q" }}, - "severity": {{ if eq .Severity "UNKNOWN" -}} - "Unknown" - {{- else if eq .Severity "LOW" -}} - "Low" - {{- else if eq .Severity "MEDIUM" -}} - "Medium" - {{- else if eq .Severity "HIGH" -}} - "High" - {{- else if eq .Severity "CRITICAL" -}} - "Critical" - {{- else -}} - "{{ .Severity }}" - {{- end }}, - "solution": {{ if .FixedVersion -}} - "Upgrade {{ .PkgName }} to {{ .FixedVersion }}" - {{- else -}} - "No solution provided" - {{- end }}, - "location": { - "dependency": { - "package": { - "name": "{{ .PkgName }}" - }, - "version": "{{ .InstalledVersion }}" - }, - {{- /* TODO: No mapping available - https://github.com/aquasecurity/trivy/issues/332 */}} - "operating_system": "{{ $os }}", - "image": "{{ $image }}" - }, - "identifiers": [ - { - {{- /* TODO: Type not extractable - https://github.com/aquasecurity/trivy-db/pull/24 */}} - "type": "cve", - "name": "{{ .VulnerabilityID }}", - "value": "{{ .VulnerabilityID }}" - {{- /* cf. https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/e3d280d7f0862ca66a1555ea8b24016a004bb914/dist/container-scanning-report-format.json#L157-179 */}} - {{- if .PrimaryURL | regexMatch "^(https?|ftp)://.+" -}}, - "url": "{{ .PrimaryURL }}" - {{- end }} - } - ], - "links": [ - {{- $l_first := true -}} - {{- range .References -}} - {{- if $l_first -}} - {{- $l_first = false }} - {{- else -}} - , - {{- end -}} - {{- if . | regexMatch "^(https?|ftp)://.+" -}} - { - "url": "{{ . }}" - } - {{- else -}} - {{- $l_first = true }} - {{- end -}} - {{- end }} - ] - } - {{- end -}} - {{- end }} - ], - "remediations": [] -} diff --git a/contrib/html.tpl b/contrib/html.tpl deleted file mode 100644 index e92b1b1..0000000 --- a/contrib/html.tpl +++ /dev/null @@ -1,148 +0,0 @@ - - - - -{{- if . }} - - {{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }} - - - -

{{- escapeXML ( index . 0 ).Target }} - Trivy Report - {{ now }}

- - {{- range . }} - - {{- if (eq (len .Vulnerabilities) 0) }} - - {{- else }} - - - - - - - - - {{- range .Vulnerabilities }} - - - - - - - - - {{- end }} - {{- end }} - {{- if (eq (len .Misconfigurations ) 0) }} - - {{- else }} - - - - - - - - {{- range .Misconfigurations }} - - - - - - - - {{- end }} - {{- end }} - {{- end }} -
{{ .Type | toString | escapeXML }}
No Vulnerabilities found
PackageVulnerability IDSeverityInstalled VersionFixed VersionLinks
{{ escapeXML .PkgName }}{{ escapeXML .VulnerabilityID }}{{ escapeXML .Vulnerability.Severity }}{{ escapeXML .InstalledVersion }}{{ escapeXML .FixedVersion }}
No Misconfigurations found
TypeMisconf IDCheckSeverityMessage
{{ escapeXML .Type }}{{ escapeXML .ID }}{{ escapeXML .Title }}{{ escapeXML .Severity }}
-{{- else }} - - -

Trivy Returned Empty Report

-{{- end }} - - diff --git a/contrib/junit.tpl b/contrib/junit.tpl deleted file mode 100644 index e4313c1..0000000 --- a/contrib/junit.tpl +++ /dev/null @@ -1,75 +0,0 @@ - - -{{- range . -}} -{{- $failures := len .Vulnerabilities }} - - {{- if not (eq .Type "") }} - - - - {{- end -}} - {{ range .Vulnerabilities }} - - {{ escapeXML .Description }} - - {{- end }} - - -{{- $target := .Target }} -{{- if .MisconfSummary }} - -{{- else }} - -{{- end }} - {{- if not (eq .Type "") }} - - - - {{- end -}} - {{ range .Misconfigurations }} - - {{- if (eq .Status "FAIL") }} - - {{- $target }}: - {{- with .CauseMetadata }} - {{- .StartLine }} - {{- if lt .StartLine .EndLine }}:{{ .EndLine }}{{ end }}: Occurrences: - {{- range $i := .Occurrences -}} - via {{ .Filename }}: - {{- .Location.StartLine }} - {{- if lt .Location.StartLine .Location.EndLine }}:{{ .Location.EndLine }}{{ end }} ({{ .Resource }}) - {{- end -}} - Code: - {{- range .Code.Lines }} - {{- if .IsCause }}{{ escapeXML .Content }} {{- end }} - {{- end }} - {{- end }} - {{- escapeXML .Description }} - - {{- end }} - - {{- end }} - - -{{- if .Licenses }} - {{- $licenses := len .Licenses }} - {{ range .Licenses }} - - - - {{- end }} - -{{- end }} - -{{- if .Secrets }} - {{- $secrets := len .Secrets }} - {{ range .Secrets }} - - {{ escapeXML .Match }} - - {{- end }} - -{{- end }} - -{{- end }} - diff --git a/scripts/install_cosign.sh b/scripts/install_cosign.sh index 6cb9174..456d34c 100755 --- a/scripts/install_cosign.sh +++ b/scripts/install_cosign.sh @@ -37,7 +37,7 @@ case "$ARCH" in ;; esac -for cmd in curl openssl install go asdf; do +for cmd in curl openssl install go jq; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "Error: $cmd is required but not found in PATH" >&2 exit 1 @@ -80,7 +80,6 @@ download "${BASE_URL}/${BINARY_NAME}-kms.sigstore.json" "$SIGSTORE_PATH" # install tuf-client go install github.com/theupdateframework/go-tuf/cmd/tuf-client@latest -asdf reshim golang # setup tuf-client SIGSTORE_ROOT_PATH="$TMP_DIR/sigstore-root.json" From e2329ac792d75d890b5b02ca3abc28ed296bf76d Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 18:57:23 +0000 Subject: [PATCH 06/13] build trivy another way --- .devcontainer/Dockerfile | 4 ++- .github/workflows/build_all_images.yml | 31 ------------------- .github/workflows/build_multi_arch_image.yml | 9 ++---- .../trivy/Dockerfile.amd64 | 9 ++++-- src/trivy/Dockerfile.arm64 | 13 ++++++++ 5 files changed, 25 insertions(+), 41 deletions(-) rename .devcontainer/Dockerfile.test => src/trivy/Dockerfile.amd64 (61%) create mode 100644 src/trivy/Dockerfile.arm64 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 177b6d8..a6e9a4c 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -74,7 +74,9 @@ RUN git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets && \ mkdir -p /usr/share/secrets-scanner && \ chmod 755 /usr/share/secrets-scanner && \ curl -L https://raw.githubusercontent.com/NHSDigital/software-engineering-quality-framework/main/tools/nhsd-git-secrets/nhsd-rules-deny.txt -o /usr/share/secrets-scanner/nhsd-rules-deny.txt -COPY --from=build /tmp/trivy_amd64/trivy /usr/local/bin/trivy + +COPY --from=build /tmp/trivy_${TARGETARCH}/trivy /usr/local/bin/trivy + USER vscode ENV PATH="/home/vscode/.asdf/shims:/home/vscode/.local/bin:$PATH:/workspaces/eps-devcontainers/node_modules/.bin" diff --git a/.github/workflows/build_all_images.yml b/.github/workflows/build_all_images.yml index 2f3db5b..fe3279a 100644 --- a/.github/workflows/build_all_images.yml +++ b/.github/workflows/build_all_images.yml @@ -33,40 +33,9 @@ jobs: echo "node_24_languages=$node_24_language_folders" echo "projects=$project_folders" } >> "$GITHUB_OUTPUT" - download_trivy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd - - name: Install cosign - run: | - ./scripts/install_cosign.sh - env: - INSTALL_DIR: ${HOME}/.local/bin - - name: Get amd64 trivy - run: | - ./scripts/install_trivy.sh - env: - INSTALL_DIR: trivy_amd64 - ARCH: 64bit - - name: Get arm64 trivy - run: | - ./scripts/install_trivy.sh - env: - INSTALL_DIR: trivy_arm64 - ARCH: ARM64 - - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f - name: Upload trivy - with: - name: "trivy" - path: | - trivy_amd64/trivy - trivy_arm64/trivy package_base_docker_image: uses: ./.github/workflows/build_multi_arch_image.yml - needs: [ - download_trivy - ] with: tag_latest: ${{ inputs.tag_latest }} docker_tag: ${{ inputs.docker_tag }} diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index b2e8000..c8a921e 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -63,14 +63,11 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd with: fetch-depth: 0 - - name: Download trivy - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c - with: - name: trivy - name: setup trivy run: | - sudo cp "trivy/trivy_${ARCH}/trivy" /usr/local/bin/ - chmod +x /usr/local/bin/trivy + docker build --output=/usr/local/bin/ -f "src/trivy/Dockerfile.${ARCH}" . + env: + ARCH: '${{ matrix.arch }}' - name: setup node uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f with: diff --git a/.devcontainer/Dockerfile.test b/src/trivy/Dockerfile.amd64 similarity index 61% rename from .devcontainer/Dockerfile.test rename to src/trivy/Dockerfile.amd64 index c021539..9616322 100644 --- a/.devcontainer/Dockerfile.test +++ b/src/trivy/Dockerfile.amd64 @@ -1,4 +1,4 @@ -FROM golang:1.26.1-bookworm +FROM golang:1.26.1-bookworm AS build RUN apt-get update && apt-get install -y \ jq \ && apt-get clean \ @@ -6,5 +6,8 @@ RUN apt-get update && apt-get install -y \ COPY scripts/install_cosign.sh /tmp/install_cosign.sh COPY scripts/install_trivy.sh /tmp/install_trivy.sh RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh -RUN INSTALL_DIR=trivy_arm64 ARCH=ARM64 /tmp/install_trivy.sh -RUN INSTALL_DIR=trivy_amd64 ARCH=64bit /tmp/install_trivy.sh +RUN INSTALL_DIR=/tmp/trivy/ ARCH=64bit /tmp/install_trivy.sh + +FROM scratch +COPY --from=build /tmp/trivy/trivy / +ENTRYPOINT ["/trivy"] diff --git a/src/trivy/Dockerfile.arm64 b/src/trivy/Dockerfile.arm64 new file mode 100644 index 0000000..379dc5d --- /dev/null +++ b/src/trivy/Dockerfile.arm64 @@ -0,0 +1,13 @@ +FROM golang:1.26.1-bookworm AS build +RUN apt-get update && apt-get install -y \ + jq \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY scripts/install_cosign.sh /tmp/install_cosign.sh +COPY scripts/install_trivy.sh /tmp/install_trivy.sh +RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh +RUN INSTALL_DIR=/tmp/trivy/ ARCH=ARM64 /tmp/install_trivy.sh + +FROM scratch +COPY --from=build /tmp/trivy/trivy / +ENTRYPOINT ["/trivy"] From 3de3b9f6e4deb9c44f765f3e92498821332be541 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 19:06:54 +0000 Subject: [PATCH 07/13] add phony --- Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile b/Makefile index 3bb8ed9..f3369e6 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ guard-%: exit 1; \ fi +.PHONY: install install-python install-node install-hooks build-base-image build-node-24-image build-node-24-python-3-10-image build-node-24-python-3-12-image build-node-24-python-3-13-image build-node-24-python-3-14-image \ + build-eps-storage-terraform-image build-fhir-facade-image build-node-24-python-3-14-golang-1-24-image build-node-24-python-3-14-java-24-image \ + build-regression-tests-image build-all build-image build-githubactions-image scan-image scan-image-json shell-image lint test lint-githubactions lint-githubaction-scripts github-login clean install: install-python install-node install-hooks install-python: From aaffd27c9a26696ba529cf7cec83193a0be9713b Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 19:16:37 +0000 Subject: [PATCH 08/13] do not use common --- Makefile | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Makefile b/Makefile index f3369e6..f5767ed 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,4 @@ CONTAINER_PREFIX=ghcr.io/nhsdigital/eps-devcontainers/ -include src/base/.devcontainer/Mk/build.mk -include src/base/.devcontainer/Mk/check.mk -include src/base/.devcontainer/Mk/trivy.mk -include src/base/.devcontainer/Mk/credentials.mk ifeq ($(strip $(NO_CACHE)),true) NO_CACHE_FLAG=--no-cache @@ -136,13 +132,9 @@ test: lint-githubactions: actionlint -github-login: - gh auth login --scopes read:packages - lint-githubaction-scripts: shellcheck .github/scripts/*.sh clean: rm -rf .out find . -type f -name '.trivyignore_combined.yaml' -delete - From 1f5d1b86c189f0414d46093c9b0474bfd40b5df6 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Fri, 20 Mar 2026 19:19:10 +0000 Subject: [PATCH 09/13] fix it --- LICENSE | 222 +--------- README.md | 488 +++++++++++++++------ package-lock.json | 1034 +-------------------------------------------- package.json | 3 +- 4 files changed, 381 insertions(+), 1366 deletions(-) diff --git a/LICENSE b/LICENSE index 261eeb9..0ba95e8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,21 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. +MIT License + +Crown Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 756d7d6..388b233 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,375 @@ -
- - -[![GitHub Release][release-img]][release] -[![Test][test-img]][test] -[![Go Report Card][go-report-img]][go-report] -[![License: Apache-2.0][license-img]][license] -[![GitHub Downloads][github-downloads-img]][release] -![Docker Pulls][docker-pulls] - -[📖 Documentation][docs] -
- -Trivy ([pronunciation][pronunciation]) is a comprehensive and versatile security scanner. -Trivy has *scanners* that look for security issues, and *targets* where it can find those issues. - -Targets (what Trivy can scan): - -- Container Image -- Filesystem -- Git Repository (remote) -- Virtual Machine Image -- Kubernetes - -Scanners (what Trivy can find there): - -- OS packages and software dependencies in use (SBOM) -- Known vulnerabilities (CVEs) -- IaC issues and misconfigurations -- Sensitive information and secrets -- Software licenses - -Trivy supports most popular programming languages, operating systems, and platforms. For a complete list, see the [Scanning Coverage] page. - -To learn more, go to the [Trivy homepage][homepage] for feature highlights, or to the [Documentation site][docs] for detailed information. - -## Quick Start - -### Get Trivy - -Trivy is available in most common distribution channels. The full list of installation options is available in the [Installation] page. Here are a few popular examples: - -- `brew install trivy` -- `docker run aquasec/trivy` -- Download binary from -- See [Installation] for more - -Trivy is integrated with many popular platforms and applications. The complete list of integrations is available in the [Ecosystem] page. Here are a few popular examples: - -- [GitHub Actions](https://github.com/aquasecurity/trivy-action) -- [Kubernetes operator](https://github.com/aquasecurity/trivy-operator) -- [VS Code plugin](https://github.com/aquasecurity/trivy-vscode-extension) -- See [Ecosystem] for more - -### Canary builds -There are canary builds ([Docker Hub](https://hub.docker.com/r/aquasec/trivy/tags?page=1&name=canary), [GitHub](https://github.com/aquasecurity/trivy/pkgs/container/trivy/75776514?tag=canary), [ECR](https://gallery.ecr.aws/aquasecurity/trivy#canary) images and [binaries](https://github.com/aquasecurity/trivy/actions/workflows/canary.yaml)) generated with every push to the main branch. - -Please be aware: canary builds might have critical bugs, so they are not recommended for use in production. - -### General usage - -```bash -trivy [--scanners ] +EPS DEV CONTAINERS +================== + +## Index +- [Introduction](#introduction) +- [Using the images](#using-the-images) + - [Project setup](#project-setup) + - [Getting image name and version in GitHub Actions from local config](#getting-image-name-and-version-in-github-actions) + - [Using images in GitHub Actions](#using-images-in-github-actions) + - [Using local or pull request images in Visual Studio Code and GitHub Actions](#using-local-or-pull-request-images-in-visual-studio-code-and-github-actions) +- [Common Makefile targets](#common-makefile-targets) + - [Defined Targets](#targets) + +- [Project structure](#project-structure) +- [Pull requests and merge to main process](#pull-requests-and-merge-to-main-process) +- [Release workflow](#release-workflow) +- [Local testing](#local-testing) + - [Building images](#building-images) + - [Scanning images](#scanning-images) + - [Interactive shell on image](#interactive-shell-on-image) +- [Generating a .trivyignore file](#generating-a-trivyignore-file) +- [Cleaning up unused container images](#cleaning-up-unused-container-images) + +# Introduction +This repository contains code to build VS Code devcontainers that can be used as a base image for all EPS projects. +Images are built for AMD64 and ARM64, and a manifest file is created that can be pulled for both architectures. This is then pushed to GitHub Container Registry and an attestation created that can be used to verify the images before being used. +Images are built using https://github.com/devcontainers/cli. + +We build a base image based on mcr.microsoft.com/devcontainers/base:ubuntu-22.04 that other images are then based on + +The base image contains + - latest os packages + - asdf + - aws cli + - aws sam cli + + It installs the following dev container features + - docker outside of docker + - GitHub CLI + +As the vscode user the following also happens + +asdf install and setup for these so they are available globally as vscode user + - shellcheck + - direnv + - actionlint + - ruby (for GitHub Pages) + - Trivy + - yq + +Install and setup git-secrets + +# Using the images +## Project setup +In each EPS project, `.devcontainer/Dockerfile` should be set to ``` - -Examples: - -```bash -trivy image python:3.4-alpine +ARG IMAGE_NAME=node_24_python_3_14 +ARG IMAGE_VERSION=latest +FROM ghcr.io/nhsdigital/eps-devcontainers/${IMAGE_NAME}:${IMAGE_VERSION} + +USER root +# specify DOCKER_GID to force container docker group id to match host +RUN if [ -n "${DOCKER_GID}" ]; then \ + if ! getent group docker; then \ + groupadd -g "${DOCKER_GID}" docker; \ + else \ + groupmod -g "${DOCKER_GID}" docker; \ + fi && \ + usermod -aG docker vscode; \ + fi ``` - -
-Result - -https://user-images.githubusercontent.com/1161307/171013513-95f18734-233d-45d3-aaf5-d6aec687db0e.mov - -
- -```bash -trivy fs --scanners vuln,secret,misconfig myproject/ +`.devcontainer/devcontainer.json` should be set to: ``` - -
-Result - -https://user-images.githubusercontent.com/1161307/171013917-b1f37810-f434-465c-b01a-22de036bd9b3.mov - -
- -```bash -trivy k8s --report summary cluster +{ + "name": "eps-common-workflows", + "build": { + "dockerfile": "Dockerfile", + "context": "..", + "args": { + "DOCKER_GID": "${env:DOCKER_GID:}", + "IMAGE_NAME": "node_24_python_3_14", + "IMAGE_VERSION": "local-build", + "USER_UID": "${localEnv:USER_ID:}", + "USER_GID": "${localEnv:GROUP_ID:}" + }, + "updateRemoteUserUID": false, + }, + "postAttachCommand": "git-secrets --register-aws; git-secrets --add-provider -- cat /usr/share/secrets-scanner/nhsd-rules-deny.txt", + "mounts": [ + "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.gnupg,target=/home/vscode/.gnupg,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.npmrc,target=/home/vscode/.npmrc,type=bind" + ], + "containerUser": "vscode", + "remoteEnv": { + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" + }, + "features": {}, + "customizations": { + .... + } +} ``` +Note - this file will be used in GitHub workflows to calculate the version of container to use in builds, so it must be valid JSON (no comments). +The name should be changed to match the name of the project. +IMAGE_NAME and IMAGE_VERSION should be changed as appropriate. +You should not need to add any features as these are already baked into the image -
-Result - -![k8s summary](docs/imgs/trivy-k8s.png) - -
+## Getting image name and version in GitHub Actions +This shared workflow should be used in GitHub Actions wherever you need to get the dev container name or tag. -## FAQ +verify_published_from_main_image should be set to false for testing pull request images. -### How to pronounce the name "Trivy"? +``` + get_config_values: + uses: NHSDigital/eps-common-workflows/.github/workflows/get-repo-config.yml@8404cf6e3a61ac8de4d1644e175e288aa4965815 + with: + verify_published_from_main_image: false +``` +## Using images in GitHub Actions +To use the image in GitHub Actions, you should first verify the attestation of the image and reference the image by the digest +For CI and release pipelines, you should set verify_published_from_main_image to ensure that only images published from main are used. +``` +jobs: + my_job_name: + runs-on: ubuntu-22.04 + needs: get_config_values + container: + image: ${{ needs.get_config_values.outputs.pinned_image }} + options: --user 1001:1001 --group-add 128 + defaults: + run: + shell: bash + steps: + - name: copy .tool-versions + run: | + cp /home/vscode/.tool-versions "$HOME/.tool-versions" + ... other steps .... +``` +It is important that: +- there is `options: --user 1001:1001 --group-add 128` below image to ensure it uses the correct user id and is added to the docker group +- the default shell is set to be bash +- the first step copies .tool-versions from /home/vscode to $HOME/.tool-versions +## Using local or pull request images in Visual Studio Code and GitHub Actions +You can use local or pull request images by changing IMAGE_VERSION in devcontainer.json. +For an image built locally following instructions below, you should put the IMAGE_VERSION=local-build. +For an image built from a pull request, you should put the IMAGE_VERSION=. +You can only use images built from a pull request for testing changes in GitHub Actions. + +# Common Makefile targets +There is a set of common Makefiles that are defined in `src/base/.devcontainer/Mk` and are included from `common.mk`. These are installed to /usr/local/share/eps/Mk on the base image, so they are available for all containers. + +This should be added to the end of each project's Makefile to include them +``` +%: + @$(MAKE) -f /usr/local/share/eps/Mk/common.mk $@ +``` +# Targets +The following targets are defined. These are needed for quality checks to run. Some targets are project-specific and should be overridden in the project's Makefile. + +Build targets (`build.mk`) +- `install` - placeholder target - should be overridden locally +- `install-node` - placeholder target - should be overridden locally +- `docker-build` - placeholder target - should be overridden locally +- `compile` - placeholder target - should be overridden locally + +Check targets (`check.mk`) +- `lint` - placeholder target - should be overridden locally +- `test` - placeholder target - should be overridden locally +- `shellcheck` - runs shellcheck on `scripts/*.sh` and `.github/scripts/*.sh` when files exist +- `cfn-lint` - runs `cfn-lint` against `cloudformation/**/*.yml|yaml` and `SAMtemplates/**/*.yml|yaml` +- `cdk-synth` - placeholder target - should be overridden locally +- `cfn-guard-sam-templates` - validates SAM templates against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` +- `cfn-guard-cloudformation` - validates `cloudformation` templates against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` +- `cfn-guard-cdk` - validates `cdk.out` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` +- `cfn-guard-terraform` - validates `terraform_plans` against cfn-guard rulesets and writes outputs to `.cfn_guard_out/` +- `actionlint` - runs actionlint against GitHub Actions +- `secret-scan` - runs git-secrets (including scanning history) against the repository +- `guard-` - checks if an environment variable is set and errors if it is not + +Credentials targets (`credentials.mk`) +- `aws-configure` - configures an AWS SSO session +- `aws-login` - Authorizes an SSO session with AWS so AWS CLI tools can be used. You may still need to set AWS_PROFILE before running commands +- `github-login` - Authorizes GitHub CLI to GitHub with scope to read packages +- `create-npmrc` - depends on `github-login`, then writes `.npmrc` with a GitHub Packages auth token and `@nhsdigital` registry + +Trivy targets (`trivy.mk`) +- `trivy-license-check` - runs Trivy license scan (HIGH/CRITICAL) and writes `.trivy_out/license_scan.txt` +- `trivy-generate-sbom` - generates CycloneDX SBOM at `.trivy_out/sbom.cdx.json` +- `trivy-scan-python` - scans Python dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_python.txt` +- `trivy-scan-node` - scans Node dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_node.txt` +- `trivy-scan-go` - scans Go dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_go.txt` +- `trivy-scan-java` - scans Java dependencies (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_java.txt` +- `trivy-scan-docker` - scans a built image (HIGH/CRITICAL) and writes `.trivy_out/dependency_results_docker.txt` (requires `DOCKER_IMAGE`), for example: + +# Project structure +We have 5 types of dev container. These are defined under src + +`base` - this is the base image that all others are based on. +`base_node` - images that install node - most language projects rely on one of these +`languages` - this installs specific versions of python - normally based off a node image +`projects` - this is used for projects where more customization is needed than just a base language image. +`githubactions` - this just takes an existing image and remaps vscode user to be 1001 so it can be used by GitHub Actions. + +Each image to be built contains a .devcontainer folder that defines how the devcontainer should be built. At a minimum, this should contain a devcontainer.json file. See https://containers.dev/implementors/json_reference/ for options for this + +Images under languages should point to a Dockerfile under src/common or src/common_node_24 that is based off the base or node image. This also runs `.devcontainer/scripts/root_install.sh` and `.devcontainer/scripts/vscode_install.sh` as vscode user as part of the build. These files should be in the language specific folder. + +We use Trivy to scan for vulnerabilities in the built Docker images. Known vulnerabilities in the base image are in `src/common/.trivyignore.yaml`. Vulnerabilities in specific images are in `.trivyignore.yaml` files in each image folder. These are combined before running a scan to exclude all known vulnerabilities + +# Pull requests and merge to main process +For each pull request, and merge to main, images are built and scanned using Trivy, and pushed to GitHub Container Registry. +Docker images are built for AMD64 and ARM64 architecture, and a combined manifest is created and pushed as part of the build. +The main images have a vscode user with ID 1000. A separately tagged image is also created with the vscode user mapped to user ID 1001 so it can be used by GitHub Actions. + +The base image is built first, and then language images, and finally project images. + +Docker images are scanned for vulnerabilities using Trivy as part of a build step, and the build fails if vulnerabilities are found that are not in the .trivyignore file. + +For pull requests, images are tagged with the pr-{pull request id}-{short commit sha}. +For merges to main, images are tagged with the ci-{short commit sha}. +GitHub Actions images are tagged with githubactions-{full tag} +AMD64 images are tagged with {tag}-amd64 +ARM64 images are tagged with {tag}-arm64 +The combined image manifest is tagged with {tag}, so it can be included in devcontainer.json and the correct image is pulled based on the host architecture. + +When a pull request is merged to main or closed, all associated images are deleted from the registry using the GitHub workflow delete_old_images + +# Release workflow +There is a release workflow that runs weekly at 18:00 on Thursday and on demand. +This creates a new release tag, builds all images, and pushes them to GitHub Container Registry. +Images are tagged with the release tag, and also with latest + +# Local testing +## Building images +You can use these commands to build images + +Base image +``` +CONTAINER_NAME=base \ + BASE_VERSION_TAG=latest \ + BASE_FOLDER=. \ + IMAGE_TAG=local-build \ + make build-image +``` +Base node 24 image +``` +CONTAINER_NAME=node_24 \ + BASE_VERSION_TAG=local-build \ + BASE_FOLDER=base_node \ + IMAGE_TAG=local-build \ + make build-image +``` +Language images +``` +CONTAINER_NAME=node_24_python_3_14 \ + BASE_VERSION_TAG=local-build \ + BASE_FOLDER=languages \ + IMAGE_TAG=local-build \ + make build-image +``` +Project images +``` +CONTAINER_NAME=fhir_facade_api \ + BASE_VERSION_TAG=local-build \ + BASE_FOLDER=projects \ + IMAGE_TAG=local-build \ + make build-image +``` +GitHub Actions image +``` +BASE_IMAGE_NAME=base \ + BASE_IMAGE_TAG=local-build \ + IMAGE_TAG=local-build \ + make build-githubactions-image +``` +## Scanning images +You can use these commands to scan images +Base image +``` +CONTAINER_NAME=base \ + BASE_FOLDER=. \ + IMAGE_TAG=local-build \ + make scan-image +``` +Base node 24 image +``` +CONTAINER_NAME=node_24 \ + BASE_FOLDER=base_node \ + IMAGE_TAG=local-build \ + EXTRA_COMMON=common_node_24 \ + make scan-image +``` +Language images +``` +CONTAINER_NAME=node_24_python_3_14 \ + BASE_FOLDER=languages \ + IMAGE_TAG=local-build \ + EXTRA_COMMON=common_node_24 \ + make scan-image +``` +Project images +``` +CONTAINER_NAME=fhir_facade_api \ + BASE_FOLDER=projects \ + IMAGE_TAG=local-build \ + make scan-image +``` + +## Interactive shell on image +You can use this to start an interactive shell in built images +base image +``` +CONTAINER_NAME=base \ + IMAGE_TAG=local-build \ + make shell-image +``` +Language images +``` +CONTAINER_NAME=node_24_python_3_12 \ + IMAGE_TAG=local-build \ + make shell-image +``` +Project images +``` +CONTAINER_NAME=fhir_facade_api \ + IMAGE_TAG=local-build \ + make shell-image +``` +GitHub Actions image +``` +CONTAINER_NAME=base \ + IMAGE_TAG=githubactions-local-build \ + make shell-image +``` -`tri` is pronounced like **tri**gger, `vy` is pronounced like en**vy**. +# Generating a .trivyignore file +You can generate a .trivyignore file for known vulnerabilities by either downloading the JSON scan output generated by the build, or by generating it locally using the scanning images commands above with a make target of scan-image-json -## Want more? Check out Aqua +If generated locally, then the output goes into .out/scan_results_docker.json. +You can use GitHub CLI tools to download the scan output file. Replace the run ID from the URL, and the -n with the filename to download +``` +gh run download -n scan_results_docker_fhir_facade_api_arm64.json +``` -If you liked Trivy, you will love Aqua which builds on top of Trivy to provide even more enhanced capabilities for a complete security management offering. -You can find a high level comparison table specific to Trivy users [here](https://trivy.dev/docs/latest/commercial/compare/). -In addition check out the website for more information about our products and services. -If you'd like to contact Aqua or request a demo, please use this form: +Once you have the scan output, use the following to generate a new .trivyignore file called .trivyignore.new.yaml. Note this will overwrite the output file when run so it should point to a new file and the contents merged with existing .trivyignore file -## Community -Trivy is an [Aqua Security][aquasec] open source project. -Learn about our open source work and portfolio [here][oss]. -Contact us about any matter by opening a GitHub Discussion [here][discussions] +``` +poetry run python \ + scripts/trivy_to_trivyignore.py \ + --input .out/scan_results_docker.json \ + --output src/projects/fhir_facade_api/.trivyignore.new.yaml +``` -Please ensure to abide by our [Code of Conduct][code-of-conduct] during all interactions. +# Cleaning up unused container images -[test]: https://github.com/aquasecurity/trivy/actions/workflows/test.yaml -[test-img]: https://github.com/aquasecurity/trivy/actions/workflows/test.yaml/badge.svg -[go-report]: https://goreportcard.com/report/github.com/aquasecurity/trivy -[go-report-img]: https://goreportcard.com/badge/github.com/aquasecurity/trivy -[release]: https://github.com/aquasecurity/trivy/releases -[release-img]: https://img.shields.io/github/release/aquasecurity/trivy.svg?logo=github -[github-downloads-img]: https://img.shields.io/github/downloads/aquasecurity/trivy/total?logo=github -[docker-pulls]: https://img.shields.io/docker/pulls/aquasec/trivy?logo=docker&label=docker%20pulls%20%2F%20trivy -[license]: https://github.com/aquasecurity/trivy/blob/main/LICENSE -[license-img]: https://img.shields.io/badge/License-Apache%202.0-blue.svg -[homepage]: https://trivy.dev -[docs]: https://trivy.dev/docs/latest/ -[pronunciation]: #how-to-pronounce-the-name-trivy -[code-of-conduct]: https://github.com/aquasecurity/community/blob/main/CODE_OF_CONDUCT.md +There is a script to delete unused container images. This runs on every merge to main and deletes pull request images, and on a weekly schedule it deletes images created by CI. +You can run it manually using the following. Using the `dry-run` flag just shows what would be deleted -[Installation]:https://trivy.dev/docs/latest/getting-started/installation/ -[Ecosystem]: https://trivy.dev/docs/latest/ecosystem/ -[Scanning Coverage]: https://trivy.dev/docs/latest/coverage/ +``` +make github-login +# or gh auth login --scopes read:packages,delete:packages if you want to be able to delete images +bash .github/scripts/delete_unused_images.sh --delete-pr --dry-run +bash .github/scripts/delete_unused_images.sh --delete-ci --dry-run +bash .github/scripts/delete_unused_images.sh --delete-pr --delete-ci +``` -[alpine]: https://ariadne.space/2021/06/08/the-vulnerability-remediation-lifecycle-of-alpine-containers/ -[rego]: https://www.openpolicyagent.org/docs/latest/#rego -[sigstore]: https://www.sigstore.dev/ +Flags: +- `--dry-run` (`-n`) shows what would be deleted without deleting anything. +- `--delete-pr` deletes images tagged with `pr-...` or `githubactions-pr-...` only when the PR is closed. +- `--delete-ci` deletes images tagged with `ci-<8 hex sha>...` or `githubactions-ci-<8 hex sha>...`. -[aquasec]: https://aquasec.com -[oss]: https://www.aquasec.com/products/open-source-projects/ -[discussions]: https://github.com/aquasecurity/trivy/discussions +If neither `--delete-pr` nor `--delete-ci` is set, the script defaults to `--delete-pr`. diff --git a/package-lock.json b/package-lock.json index e004734..827a984 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "@devcontainers/cli": "^0.84.0", - "@tufjs/cli": "^0.4.1" + "@devcontainers/cli": "^0.84.0" } }, "node_modules/@devcontainers/cli": { @@ -24,1037 +23,6 @@ "engines": { "node": ">=20.0.0" } - }, - "node_modules/@gar/promise-retry": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@gar/promise-retry/-/promise-retry-1.0.3.tgz", - "integrity": "sha512-GmzA9ckNokPypTg10pgpeHNQe7ph+iIKKmhKu3Ob9ANkswreCx7R3cKmY781K8QK3AqVL3xVh9A42JvIAbkkSA==", - "license": "MIT", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@npmcli/agent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", - "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", - "license": "ISC", - "dependencies": { - "agent-base": "^7.1.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "lru-cache": "^11.2.1", - "socks-proxy-agent": "^8.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@npmcli/fs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-5.0.0.tgz", - "integrity": "sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==", - "license": "ISC", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@npmcli/redact": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-4.0.0.tgz", - "integrity": "sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==", - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@oclif/color": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@oclif/color/-/color-1.0.13.tgz", - "integrity": "sha512-/2WZxKCNjeHlQogCs1VBtJWlPXjwWke/9gMrwsVsrUt00g2V6LUBvwgwrxhrXepjOmq4IZ5QeNbpDMEOUlx/JA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.2.1", - "chalk": "^4.1.0", - "strip-ansi": "^6.0.1", - "supports-color": "^8.1.1", - "tslib": "^2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@oclif/core": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@oclif/core/-/core-4.10.0.tgz", - "integrity": "sha512-T52mrztDvj7RXiURdA+6vd1B4yeYVpHF6DH/RshqEwIA6R63dhe4YoIBYWJi0H3LhCQTUTL84uWadrJAImu/vg==", - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.3.2", - "ansis": "^3.17.0", - "clean-stack": "^3.0.1", - "cli-spinners": "^2.9.2", - "debug": "^4.4.3", - "ejs": "^3.1.10", - "get-package-type": "^0.1.0", - "indent-string": "^4.0.0", - "is-wsl": "^2.2.0", - "lilconfig": "^3.1.3", - "minimatch": "^10.2.4", - "semver": "^7.7.3", - "string-width": "^4.2.3", - "supports-color": "^8", - "tinyglobby": "^0.2.14", - "widest-line": "^3.1.0", - "wordwrap": "^1.0.0", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@oclif/plugin-help": { - "version": "6.2.38", - "resolved": "https://registry.npmjs.org/@oclif/plugin-help/-/plugin-help-6.2.38.tgz", - "integrity": "sha512-aTVQ8qPy5kD/Neq2B4OEo2joukHWdEabTMHfQyXtsagW1O2MvhM58+JUWADvieX67OSjSXseD6f6O/e5SA2N/Q==", - "license": "MIT", - "dependencies": { - "@oclif/core": "^4" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@tufjs/canonical-json": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz", - "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==", - "license": "MIT", - "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/@tufjs/cli": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@tufjs/cli/-/cli-0.4.1.tgz", - "integrity": "sha512-LVfLmIDj8GaDr5h614XP/xc2KSkSU9XbOTprsKUtz4XmbzXNaxCwMm1u90eFP29PB66MKb/BGCiAUcEf75UScQ==", - "license": "MIT", - "dependencies": { - "@oclif/color": "^1.0.13", - "@oclif/core": "^4", - "@oclif/plugin-help": "^6", - "make-fetch-happen": "^15.0.1", - "tuf-js": "4.1.0" - }, - "bin": { - "tuf": "bin/run" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/@tufjs/models": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-4.1.0.tgz", - "integrity": "sha512-Y8cK9aggNRsqJVaKUlEYs4s7CvQ1b1ta2DVPyAimb0I2qhzjNk+A+mxvll/klL0RlfuIUei8BF7YWiua4kQqww==", - "license": "MIT", - "dependencies": { - "@tufjs/canonical-json": "2.0.0", - "minimatch": "^10.1.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ansis": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-3.17.0.tgz", - "integrity": "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg==", - "license": "ISC", - "engines": { - "node": ">=14" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/cacache": { - "version": "20.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.4.tgz", - "integrity": "sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==", - "license": "ISC", - "dependencies": { - "@npmcli/fs": "^5.0.0", - "fs-minipass": "^3.0.0", - "glob": "^13.0.0", - "lru-cache": "^11.1.0", - "minipass": "^7.0.3", - "minipass-collect": "^2.0.1", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "p-map": "^7.0.2", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/clean-stack": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-3.0.1.tgz", - "integrity": "sha512-lR9wNiMRcVQjSB3a7xXGLuz4cr4wJuuXlaAEbRutGowQTmlp7R72/DOgN21e8jdwblMWl9UOJMJXarX94pzKdg==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/ejs": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", - "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", - "license": "Apache-2.0", - "dependencies": { - "jake": "^10.8.5" - }, - "bin": { - "ejs": "bin/cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/filelist": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", - "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fs-minipass": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz", - "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/iconv-lite": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", - "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", - "license": "MIT", - "optional": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ip-address": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", - "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jake": { - "version": "10.9.4", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", - "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.6", - "filelist": "^1.0.4", - "picocolors": "^1.1.1" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lru-cache": { - "version": "11.2.7", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", - "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/make-fetch-happen": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.5.tgz", - "integrity": "sha512-uCbIa8jWWmQZt4dSnEStkVC6gdakiinAm4PiGsywIkguF0eWMdcjDz0ECYhUolFU3pFLOev9VNPCEygydXnddg==", - "license": "ISC", - "dependencies": { - "@gar/promise-retry": "^1.0.0", - "@npmcli/agent": "^4.0.0", - "@npmcli/redact": "^4.0.0", - "cacache": "^20.0.1", - "http-cache-semantics": "^4.1.1", - "minipass": "^7.0.2", - "minipass-fetch": "^5.0.0", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^1.0.0", - "proc-log": "^6.0.0", - "ssri": "^13.0.0" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-collect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz", - "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minipass-fetch": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.2.tgz", - "integrity": "sha512-2d0q2a8eCi2IRg/IGubCNRJoYbA1+YPXAzQVRFmB45gdGZafyivnZ5YSEfo3JikbjGxOdntGFvBQGqaSMXlAFQ==", - "license": "MIT", - "dependencies": { - "minipass": "^7.0.3", - "minipass-sized": "^2.0.0", - "minizlib": "^3.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - }, - "optionalDependencies": { - "iconv-lite": "^0.7.2" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-2.0.0.tgz", - "integrity": "sha512-zSsHhto5BcUVM2m1LurnXY6M//cGhVaegT71OfOXoprxT6o780GZd792ea6FfrQkuU4usHZIUczAQMRUE2plzA==", - "license": "ISC", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", - "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", - "license": "MIT", - "dependencies": { - "minipass": "^7.1.2" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", - "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/p-map": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", - "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/proc-log": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", - "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", - "license": "ISC", - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "optional": true - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", - "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", - "license": "MIT", - "dependencies": { - "ip-address": "^10.0.1", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", - "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "socks": "^2.8.3" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/ssri": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.1.tgz", - "integrity": "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ==", - "license": "ISC", - "dependencies": { - "minipass": "^7.0.3" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tuf-js": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-4.1.0.tgz", - "integrity": "sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==", - "license": "MIT", - "dependencies": { - "@tufjs/models": "4.1.0", - "debug": "^4.4.3", - "make-fetch-happen": "^15.0.1" - }, - "engines": { - "node": "^20.17.0 || >=22.9.0" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "license": "MIT", - "dependencies": { - "string-width": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" } } } diff --git a/package.json b/package.json index efb687e..44a8da5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "license": "ISC", "description": "", "dependencies": { - "@devcontainers/cli": "^0.84.0", - "@tufjs/cli": "^0.4.1" + "@devcontainers/cli": "^0.84.0" } } From dc351a9cce31263f2a8d1d39ba6ebcbb394c5844 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Sat, 21 Mar 2026 09:09:17 +0000 Subject: [PATCH 10/13] correct arch --- scripts/install_cosign.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/install_cosign.sh b/scripts/install_cosign.sh index 456d34c..d0d3d4f 100755 --- a/scripts/install_cosign.sh +++ b/scripts/install_cosign.sh @@ -31,6 +31,9 @@ case "$ARCH" in x86_64|amd64) BINARY_NAME="cosign-linux-amd64" ;; + aarch64|arm64) + BINARY_NAME="cosign-linux-arm64" + ;; *) echo "Error: Unsupported architecture $ARCH" >&2 exit 1 From 3bd546c138afcc879666b2e9c0bb539669ed7131 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Sat, 21 Mar 2026 10:03:17 +0000 Subject: [PATCH 11/13] move to 1 trivy location --- .devcontainer/Dockerfile | 16 ++++++++++------ .github/workflows/build_multi_arch_image.yml | 2 +- src/base/.devcontainer/.tool-versions | 1 - src/base/.devcontainer/Dockerfile | 18 ++++++++++++++++++ .../.devcontainer/Dockerfile.trivy.amd64} | 0 .../.devcontainer/Dockerfile.trivy.arm64} | 0 .../.devcontainer/scripts}/install_cosign.sh | 0 .../.devcontainer/scripts}/install_trivy.sh | 4 +--- 8 files changed, 30 insertions(+), 11 deletions(-) rename src/{trivy/Dockerfile.amd64 => base/.devcontainer/Dockerfile.trivy.amd64} (100%) rename src/{trivy/Dockerfile.arm64 => base/.devcontainer/Dockerfile.trivy.arm64} (100%) rename {scripts => src/base/.devcontainer/scripts}/install_cosign.sh (100%) rename {scripts => src/base/.devcontainer/scripts}/install_trivy.sh (90%) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a6e9a4c..06022c9 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -3,11 +3,15 @@ RUN apt-get update && apt-get install -y \ jq \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -COPY scripts/install_cosign.sh /tmp/install_cosign.sh -COPY scripts/install_trivy.sh /tmp/install_trivy.sh -RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh -RUN INSTALL_DIR=/tmp/trivy_arm64 ARCH=ARM64 /tmp/install_trivy.sh -RUN INSTALL_DIR=/tmp/trivy_amd64 ARCH=64bit /tmp/install_trivy.sh +COPY src/base/.devcontainer/scripts/install_cosign.sh /tmp/install_cosign.sh +COPY src/base/.devcontainer/scripts/install_trivy.sh /tmp/install_trivy.sh +RUN case "${TARGETARCH}" in \ + x86_64|amd64) TRIVY_ARCH=64bit ;; \ + aarch64|arm64) TRIVY_ARCH=ARM64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \ + esac \ + && INSTALL_DIR=/tmp/trivy/ ARCH="${TRIVY_ARCH}" /tmp/install_trivy.sh + FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 ARG TARGETARCH @@ -75,7 +79,7 @@ RUN git clone https://github.com/awslabs/git-secrets.git /tmp/git-secrets && \ chmod 755 /usr/share/secrets-scanner && \ curl -L https://raw.githubusercontent.com/NHSDigital/software-engineering-quality-framework/main/tools/nhsd-git-secrets/nhsd-rules-deny.txt -o /usr/share/secrets-scanner/nhsd-rules-deny.txt -COPY --from=build /tmp/trivy_${TARGETARCH}/trivy /usr/local/bin/trivy +COPY --from=build /tmp/trivy/trivy /usr/local/bin/trivy USER vscode diff --git a/.github/workflows/build_multi_arch_image.yml b/.github/workflows/build_multi_arch_image.yml index c8a921e..11c1857 100644 --- a/.github/workflows/build_multi_arch_image.yml +++ b/.github/workflows/build_multi_arch_image.yml @@ -65,7 +65,7 @@ jobs: fetch-depth: 0 - name: setup trivy run: | - docker build --output=/usr/local/bin/ -f "src/trivy/Dockerfile.${ARCH}" . + docker build --output=/usr/local/bin/ -f "src/base/.devcontainer/Dockerfile.trivy.${ARCH}" . env: ARCH: '${{ matrix.arch }}' - name: setup node diff --git a/src/base/.devcontainer/.tool-versions b/src/base/.devcontainer/.tool-versions index bac5b7b..7aaf5f8 100644 --- a/src/base/.devcontainer/.tool-versions +++ b/src/base/.devcontainer/.tool-versions @@ -2,5 +2,4 @@ shellcheck 0.11.0 direnv 2.37.1 actionlint 1.7.11 ruby 3.3.0 -trivy 0.69.3 yq 4.52.4 diff --git a/src/base/.devcontainer/Dockerfile b/src/base/.devcontainer/Dockerfile index 1d39021..69d2bf8 100644 --- a/src/base/.devcontainer/Dockerfile +++ b/src/base/.devcontainer/Dockerfile @@ -1,3 +1,19 @@ +FROM golang:1.26.1-bookworm AS build +ARG TARGETARCH +RUN apt-get update && apt-get install -y \ + jq \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* +COPY scripts/install_cosign.sh /tmp/install_cosign.sh +COPY scripts/install_trivy.sh /tmp/install_trivy.sh +RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh +RUN case "${TARGETARCH}" in \ + x86_64|amd64) TRIVY_ARCH=64bit ;; \ + aarch64|arm64) TRIVY_ARCH=ARM64 ;; \ + *) echo "Unsupported TARGETARCH: ${TARGETARCH}" && exit 1 ;; \ + esac \ + && INSTALL_DIR=/tmp/trivy/ ARCH="${TRIVY_ARCH}" /tmp/install_trivy.sh + FROM mcr.microsoft.com/devcontainers/base:ubuntu-22.04 ARG SCRIPTS_DIR=/usr/local/share/eps @@ -16,6 +32,8 @@ COPY --chmod=755 Mk ${SCRIPTS_DIR}/Mk WORKDIR ${SCRIPTS_DIR}/${CONTAINER_NAME} RUN ./root_install.sh +COPY --from=build /tmp/trivy/trivy /usr/local/bin/trivy + COPY --chmod=755 scripts/vscode_install.sh ${SCRIPTS_DIR}/${CONTAINER_NAME}/vscode_install.sh USER vscode COPY --chown=vscode:vscode .tool-versions.asdf /home/vscode/.tool-versions.asdf diff --git a/src/trivy/Dockerfile.amd64 b/src/base/.devcontainer/Dockerfile.trivy.amd64 similarity index 100% rename from src/trivy/Dockerfile.amd64 rename to src/base/.devcontainer/Dockerfile.trivy.amd64 diff --git a/src/trivy/Dockerfile.arm64 b/src/base/.devcontainer/Dockerfile.trivy.arm64 similarity index 100% rename from src/trivy/Dockerfile.arm64 rename to src/base/.devcontainer/Dockerfile.trivy.arm64 diff --git a/scripts/install_cosign.sh b/src/base/.devcontainer/scripts/install_cosign.sh similarity index 100% rename from scripts/install_cosign.sh rename to src/base/.devcontainer/scripts/install_cosign.sh diff --git a/scripts/install_trivy.sh b/src/base/.devcontainer/scripts/install_trivy.sh similarity index 90% rename from scripts/install_trivy.sh rename to src/base/.devcontainer/scripts/install_trivy.sh index 0de665a..9e0588e 100755 --- a/scripts/install_trivy.sh +++ b/src/base/.devcontainer/scripts/install_trivy.sh @@ -6,8 +6,6 @@ INSTALL_DIR="${INSTALL_DIR:-$DEFAULT_INSTALL_DIR}" VERSION="v0.69.3" DEFAULT_ARCH="64bit" ARCH="${ARCH:-$DEFAULT_ARCH}" -#trivy_0.69.3_Linux-64bit.tar.gz -#trivy_0.69.3_Linux-ARM64.tar.gz RELEASE_NUMBER="${VERSION#v}" BASE_URL="https://github.com/aquasecurity/trivy/releases/download/${VERSION}" ARCHIVE="trivy_${RELEASE_NUMBER}_Linux-${ARCH}.tar.gz" @@ -18,7 +16,7 @@ usage() { cat <<'EOF' Usage: install_trivy.sh [output_dir] -Downloads Trivy v0.69.3, its sigstore bundle, and checksum into output_dir (default: current directory), +Downloads Trivy, its sigstore bundle, and checksum into output_dir (default: current directory), then verifies the checksum and the sigstore bundle, following https://github.com/aquasecurity/trivy/blob/main/docs/getting-started/signature-verification.md. EOF From 859aa7a442efb5dc479fd1263f31fde46c0150f8 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Sat, 21 Mar 2026 10:23:23 +0000 Subject: [PATCH 12/13] fix path --- src/base/.devcontainer/Dockerfile.trivy.amd64 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/.devcontainer/Dockerfile.trivy.amd64 b/src/base/.devcontainer/Dockerfile.trivy.amd64 index 9616322..169855e 100644 --- a/src/base/.devcontainer/Dockerfile.trivy.amd64 +++ b/src/base/.devcontainer/Dockerfile.trivy.amd64 @@ -3,8 +3,8 @@ RUN apt-get update && apt-get install -y \ jq \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -COPY scripts/install_cosign.sh /tmp/install_cosign.sh -COPY scripts/install_trivy.sh /tmp/install_trivy.sh +COPY src/base/.devcontainer/scripts/install_cosign.sh /tmp/install_cosign.sh +COPY src/base/.devcontainer/scripts/install_trivy.sh /tmp/install_trivy.sh RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh RUN INSTALL_DIR=/tmp/trivy/ ARCH=64bit /tmp/install_trivy.sh From b1f157e473670be46ceeb6b0e77d7a98c564a442 Mon Sep 17 00:00:00 2001 From: Anthony Brown Date: Sat, 21 Mar 2026 10:33:20 +0000 Subject: [PATCH 13/13] fix local build --- .devcontainer/Dockerfile | 2 ++ .devcontainer/devcontainer.json | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 06022c9..9ef54e3 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,10 +1,12 @@ FROM golang:1.26.1-bookworm AS build +ARG TARGETARCH RUN apt-get update && apt-get install -y \ jq \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* COPY src/base/.devcontainer/scripts/install_cosign.sh /tmp/install_cosign.sh COPY src/base/.devcontainer/scripts/install_trivy.sh /tmp/install_trivy.sh +RUN INSTALL_DIR=/usr/local/bin /tmp/install_cosign.sh RUN case "${TARGETARCH}" in \ x86_64|amd64) TRIVY_ARCH=64bit ;; \ aarch64|arm64) TRIVY_ARCH=ARM64 ;; \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b08221a..645e5c2 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,8 @@ "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind", "source=${env:HOME}${env:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind", "source=${env:HOME}${env:USERPROFILE}/.gnupg,target=/home/vscode/.gnupg,type=bind", - "source=${env:HOME}${env:USERPROFILE}/.npmrc,target=/home/vscode/.npmrc,type=bind" + "source=${env:HOME}${env:USERPROFILE}/.npmrc,target=/home/vscode/.npmrc,type=bind", + "source=${env:HOME}${env:USERPROFILE}/.gitconfig,target=/home/vscode/.gitconfig,type=bind" ], "runArgs": [ "--network=host"