feat(security): implement reusable security scan workflow #1
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Reusable Security Scan | ||
on: | ||
workflow_call: | ||
inputs: | ||
skip-checkout: | ||
type: boolean | ||
default: false | ||
description: "Skip repository checkout" | ||
image-name: | ||
type: string | ||
required: false | ||
description: "Docker image name" | ||
image-tag: | ||
type: string | ||
required: true | ||
description: "Docker image tag" | ||
enable-docker-build: | ||
type: boolean | ||
default: true | ||
description: "Enable Docker build" | ||
build-args: | ||
type: string | ||
required: false | ||
description: "Docker build arguments (JSON string)" | ||
secrets: | ||
DOCKER_USERNAME: | ||
required: false | ||
DOCKER_PASSWORD: | ||
required: false | ||
LW_ACCOUNT_NAME: | ||
required: true | ||
LW_ACCESS_TOKEN: | ||
required: true | ||
GITHUB_TOKEN: | ||
Check failure on line 35 in .github/workflows/security-scan-reusable.yaml GitHub Actions / .github/workflows/security-scan-reusable.yamlInvalid workflow file
|
||
required: true | ||
ARTIFACTORY_USERNAME: | ||
required: true | ||
ARTIFACTORY_AUTH_TOKEN: | ||
required: true | ||
NEWRELIC_SECURITY_PROD_LICENSE_KEY: | ||
required: true | ||
NEWRELIC_IT_SECURITY_PROD_ACCOUNT_ID: | ||
required: true | ||
jobs: | ||
security-scan: | ||
name: Container Security Scan | ||
runs-on: [self-hosted, linux] | ||
steps: | ||
- name: Checkout Repository | ||
if: ${{ !inputs.skip-checkout }} | ||
uses: actions/[email protected] | ||
- name: Build Docker Image | ||
if: ${{ inputs.enable-docker-build }} | ||
id: docker-build | ||
uses: open-turo/actions-security/docker-build@v2 | ||
with: | ||
dockerhub-user: ${{ secrets.DOCKER_USERNAME }} | ||
dockerhub-password: ${{ secrets.DOCKER_PASSWORD }} | ||
image-version: ${{ inputs.image-tag }} | ||
build-args: ${{ inputs.build-args }} | ||
- name: Set Image Name | ||
run: | | ||
if [[ "${{ inputs.enable-docker-build }}" == "true" ]]; then | ||
echo "IMAGE_NAME=\"${{ steps.docker-build.outputs.image-name }}\"" >> "$GITHUB_ENV" | ||
else | ||
echo "IMAGE_NAME=\"${{ inputs.image-name }}\"" >> "$GITHUB_ENV" | ||
fi | ||
- name: Scan Container Image | ||
uses: lacework/[email protected] | ||
with: | ||
LW_ACCOUNT_NAME: ${{ secrets.LW_ACCOUNT_NAME }} | ||
LW_ACCESS_TOKEN: ${{ secrets.LW_ACCESS_TOKEN }} | ||
IMAGE_NAME: ${{ env.IMAGE_NAME }} | ||
IMAGE_TAG: ${{ inputs.image-tag }} | ||
SAVE_RESULTS_IN_LACEWORK: true | ||
ADDITIONAL_PARAMETERS: "-j" | ||
- name: Verify Scan Results | ||
id: verify-results | ||
run: | | ||
if [[ ! -f "results.stdout" ]]; then | ||
echo "::error::Scan results file not found" | ||
exit 1 | ||
fi | ||
if ! jq empty "results.stdout" 2>/dev/null; then | ||
echo "::error::Invalid JSON in scan results" | ||
exit 1 | ||
fi | ||
echo "scan-results-path=results.stdout" >> "$GITHUB_ENV" | ||
- name: Set up Node.js | ||
uses: actions/[email protected] | ||
with: | ||
node-version: "20.10.0" | ||
- name: Process Security Results | ||
id: results-processing | ||
uses: actions/[email protected] | ||
with: | ||
repository: turo/it-security-tooling | ||
ref: v1.0.0 | ||
token: ${{ secrets.GITHUB_TOKEN }} | ||
path: security-tooling | ||
- name: Run Security Reporter | ||
working-directory: security-tooling/security-engineering/lacework-newrelic-reporter | ||
run: | | ||
npm ci | ||
npm run build | ||
node dist/index.js --file="${{ env.scan-results-path }}" | ||
env: | ||
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEWRELIC_SECURITY_PROD_LICENSE_KEY }} | ||
NEWRELIC_ACCOUNT_ID: ${{ secrets.NEWRELIC_IT_SECURITY_PROD_ACCOUNT_ID }} | ||
LACEWORK_ACCOUNT_URL: ${{ secrets.LW_ACCOUNT_NAME }} | ||
LACEWORK_TOKEN: ${{ secrets.LW_ACCESS_TOKEN }} | ||
METRIC_PREFIX: security.container | ||
- name: Upload Results Artifact | ||
uses: actions/[email protected] | ||
with: | ||
name: security-scan-results | ||
path: ${{ env.scan-results-path }} | ||
retention-days: 30 | ||
- name: Format PR Comment | ||
if: github.event_name == 'pull_request' | ||
run: | | ||
{ | ||
echo "## Container Security Scan Results" | ||
echo "<details><summary>Click to expand</summary>" | ||
echo "<pre>" | ||
jq -r '. | | ||
"Image: \(.cve.image.image_info.repository):\(.cve.image.image_info.tags[0])\n" + | ||
"Scan Time: \(.cve.last_evaluation_time)\n\n" + | ||
"Critical: \(.cve.critical_vulnerabilities)\n" + | ||
"High: \(.cve.high_vulnerabilities)\n" + | ||
"Medium: \(.cve.medium_vulnerabilities)\n" + | ||
"Low: \(.cve.low_vulnerabilities)\n" + | ||
"Fixable: \(.cve.fixable_vulnerabilities)" | ||
' "results.stdout" | ||
echo "</pre>" | ||
echo "</details>" | ||
} > "pr-results.md" | ||
- name: Comment on PR | ||
if: github.event_name == 'pull_request' | ||
uses: thollander/[email protected] | ||
with: | ||
file-path: pr-results.md | ||
mode: recreate | ||
comment-tag: security-scan | ||
github-token: ${{ secrets.GITHUB_TOKEN }} | ||
- name: Cleanup | ||
if: always() | ||
run: | | ||
if [[ -n "${IMAGE_NAME}" ]]; then | ||
docker image rm "${IMAGE_NAME}:${{ inputs.image-tag }}" || true | ||
fi |