Security Scan #101
This file contains hidden or 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: Security Scan | |
| on: | |
| # Trigger 1: Merge to main or version branches (*.*) | |
| push: | |
| branches: | |
| - main | |
| - '*.*' | |
| # Trigger 2: Daily scheduled run at 00:13 UTC | |
| # Schedule it a random minute because most Github Actions are scheduled | |
| # at the start of the hour and their invocation can get delayed. | |
| # Ref: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule | |
| schedule: | |
| - cron: '13 0 * * *' | |
| # Trigger 3: Manual trigger | |
| workflow_dispatch: | |
| inputs: | |
| standalone-run: | |
| description: 'Run as standalone workflow (skip build workflow invocation)' | |
| required: false | |
| type: boolean | |
| default: false | |
| env: | |
| CODE_EDITOR_TARGETS: '["code-editor-sagemaker-server"]' | |
| STANDALONE_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.standalone-run || false }} | |
| jobs: | |
| get-branches-to-scan: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| security-scan-branches: ${{ steps.determine-pr-branches.outputs.branches || steps.determine-scheduled-security-scan-branches.outputs.branches }} | |
| global-dependencies-branches: ${{ steps.determine-pr-branches.outputs.branches || steps.determine-scheduled-global-dependencies-branches.outputs.branches }} | |
| output-branch-name: ${{ steps.determine-pr-branches.outputs.output-branch-name || steps.get-upstream-branches.outputs.output-branch-name }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine branches for merge events | |
| id: determine-pr-branches | |
| if: github.event_name == 'push' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| # For merge events, use the branch that was merged to | |
| echo "Merged to branch: $GITHUB_REF_NAME" | |
| if [[ "$GITHUB_REF_NAME" =~ ^[0-9]+\.[0-9]+$ ]] || [[ "$GITHUB_REF_NAME" == "main" ]]; then | |
| echo "Branch matches allowed pattern (main or digit.digit)" | |
| echo "branches=[\"$GITHUB_REF_NAME\"]" >> "$GITHUB_OUTPUT" | |
| echo "output-branch-name=$GITHUB_REF_NAME" >> "$GITHUB_OUTPUT" | |
| echo "Branches to scan: [$GITHUB_REF_NAME]" | |
| echo "Output files will use branch name: $GITHUB_REF_NAME" | |
| else | |
| echo "Branch does not match allowed pattern - no branches to scan" | |
| echo "branches=[]" >> "$GITHUB_OUTPUT" | |
| echo "output-branch-name=" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Get all upstream branches | |
| id: get-upstream-branches | |
| if: github.event_name != 'push' | |
| run: | | |
| # Get main branch and all version branches (*.*) | |
| branches=$(git branch -r | grep -E 'origin/(main|[0-9]+\.[0-9]+)$' | sed 's/origin\///' | tr '\n' ' ') | |
| echo "Found upstream branches: $branches" | |
| echo "upstream-branches=$branches" >> "$GITHUB_OUTPUT" | |
| echo "output-branch-name=scheduled" >> "$GITHUB_OUTPUT" | |
| - name: Get completed workflows from previous day | |
| id: get-completed-workflows | |
| if: github.event_name != 'push' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| workflow_name="Security Scan" | |
| # Get workflows from previous day (00:00 UTC to 23:59 UTC) | |
| previous_day_start=$(date -d 'yesterday' -u +%Y-%m-%dT00:00:00Z) | |
| previous_day_end=$(date -d 'yesterday' -u +%Y-%m-%dT23:59:59Z) | |
| echo "Getting completed workflows from previous day: $previous_day_start to $previous_day_end" | |
| # Get all completed workflow runs from previous day | |
| completed_runs=$(gh run list --workflow="$workflow_name" --json databaseId,startedAt,conclusion,headBranch --status completed --limit 100) | |
| recent_runs=$(echo "$completed_runs" | jq --arg start "$previous_day_start" --arg end "$previous_day_end" '.[] | select(.startedAt >= $start and .startedAt <= $end)') | |
| echo "Found completed workflow runs from previous day:" | |
| echo "$recent_runs" | jq -r '.databaseId' | |
| # Store workflow run IDs for artifact checking | |
| run_ids=$(echo "$recent_runs" | jq -r '.databaseId' | tr '\n' ' ') | |
| echo "workflow-run-ids=$run_ids" >> "$GITHUB_OUTPUT" | |
| - name: Check for successful scan artifacts from previous day | |
| id: check-scan-artifacts | |
| if: github.event_name != 'push' | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPOSITORY: ${{ github.repository }} | |
| run: | | |
| run_ids="${{ steps.get-completed-workflows.outputs.workflow-run-ids }}" | |
| successful_security_scan_branches="" | |
| successful_global_dependencies_branches="" | |
| echo "Checking for successful scan artifacts from workflow runs: $run_ids" | |
| for run_id in $run_ids; do | |
| if [ -n "$run_id" ]; then | |
| echo "Checking artifacts for run ID: $run_id" | |
| # Get artifacts for this run | |
| artifacts=$(gh api "/repos/$REPOSITORY/actions/runs/$run_id/artifacts" --jq '.artifacts[].name') | |
| # Check for scan-success-branch-* artifacts | |
| security_scan_artifacts=$(echo "$artifacts" | grep "^scan-success-branch-" || true) | |
| global_dependencies_artifacts=$(echo "$artifacts" | grep "^global-scan-success-" || true) | |
| # Extract branch names from artifact names | |
| for artifact in $security_scan_artifacts; do | |
| branch_name=$(echo "$artifact" | sed 's/scan-success-branch-files//' | sed 's/scan-success-branch-//') | |
| if [ -n "$branch_name" ]; then | |
| successful_security_scan_branches="$successful_security_scan_branches $branch_name" | |
| fi | |
| done | |
| for artifact in $global_dependencies_artifacts; do | |
| branch_name="${artifact/#global-scan-success-/}" | |
| if [ -n "$branch_name" ]; then | |
| successful_global_dependencies_branches="$successful_global_dependencies_branches $branch_name" | |
| fi | |
| done | |
| fi | |
| done | |
| # Remove duplicates and clean up | |
| successful_security_scan_branches=$(echo "$successful_security_scan_branches" | tr ' ' '\n' | sort -u | tr '\n' ' ') | |
| successful_global_dependencies_branches=$(echo "$successful_global_dependencies_branches" | tr ' ' '\n' | sort -u | tr '\n' ' ') | |
| echo "Branches with successful security scans from previous day: $successful_security_scan_branches" | |
| echo "Branches with successful global dependency scans from previous day: $successful_global_dependencies_branches" | |
| echo "successful-security-scan-branches=$successful_security_scan_branches" >> "$GITHUB_OUTPUT" | |
| echo "successful-global-dependencies-branches=$successful_global_dependencies_branches" >> "$GITHUB_OUTPUT" | |
| - name: Determine security scan branches for scheduled runs | |
| id: determine-scheduled-security-scan-branches | |
| if: github.event_name != 'push' | |
| run: | | |
| upstream_branches="${{ steps.get-upstream-branches.outputs.upstream-branches }}" | |
| successful_branches="${{ steps.check-scan-artifacts.outputs.successful-security-scan-branches }}" | |
| branches_to_scan="" | |
| echo "Upstream branches: $upstream_branches" | |
| echo "Successfully scanned branches from previous day: $successful_branches" | |
| # Check each upstream branch | |
| for branch in $upstream_branches; do | |
| branch=$(echo "$branch" | xargs) # trim whitespace | |
| if [ -n "$branch" ]; then | |
| # Check if this branch was successfully scanned in the previous day | |
| if echo "$successful_branches" | grep -q "\b$branch\b"; then | |
| echo "Skipping branch $branch - found successful scan from previous day" | |
| else | |
| echo "Adding branch $branch to security scan list - no successful scan from previous day" | |
| branches_to_scan="$branches_to_scan $branch" | |
| fi | |
| fi | |
| done | |
| # Clean up and convert to JSON array | |
| branches_to_scan=$(echo "$branches_to_scan" | xargs) | |
| if [ -n "$branches_to_scan" ]; then | |
| json_branches=$(echo "$branches_to_scan" | tr ' ' '\n' | jq -R . | jq -s -c .) | |
| echo "branches=$json_branches" >> "$GITHUB_OUTPUT" | |
| echo "Security scan branches to scan: $json_branches" | |
| else | |
| echo "branches=[]" >> "$GITHUB_OUTPUT" | |
| echo "No security scan branches to scan - all have successful scans from previous day" | |
| fi | |
| - name: Determine global dependencies branches for scheduled runs | |
| id: determine-scheduled-global-dependencies-branches | |
| if: github.event_name != 'push' | |
| run: | | |
| upstream_branches="${{ steps.get-upstream-branches.outputs.upstream-branches }}" | |
| successful_branches="${{ steps.check-scan-artifacts.outputs.successful-global-dependencies-branches }}" | |
| branches_to_scan="" | |
| echo "Upstream branches: $upstream_branches" | |
| echo "Successfully scanned global dependencies branches from previous day: $successful_branches" | |
| # Check each upstream branch | |
| for branch in $upstream_branches; do | |
| branch=$(echo "$branch" | xargs) # trim whitespace | |
| if [ -n "$branch" ]; then | |
| # Check if this branch was successfully scanned in the previous day | |
| if echo "$successful_branches" | grep -q "\b$branch\b"; then | |
| echo "Skipping branch $branch - found successful global dependencies scan from previous day" | |
| else | |
| echo "Adding branch $branch to global dependencies scan list - no successful scan from previous day" | |
| branches_to_scan="$branches_to_scan $branch" | |
| fi | |
| fi | |
| done | |
| # Clean up and convert to JSON array | |
| branches_to_scan=$(echo "$branches_to_scan" | xargs) | |
| if [ -n "$branches_to_scan" ]; then | |
| json_branches=$(echo "$branches_to_scan" | tr ' ' '\n' | jq -R . | jq -s -c .) | |
| echo "branches=$json_branches" >> "$GITHUB_OUTPUT" | |
| echo "Global dependencies branches to scan: $json_branches" | |
| else | |
| echo "branches=[]" >> "$GITHUB_OUTPUT" | |
| echo "No global dependencies branches to scan - all have successful scans from previous day" | |
| fi | |
| security-scan: | |
| runs-on: ubuntu-latest | |
| needs: [get-branches-to-scan] | |
| if: needs.get-branches-to-scan.outputs.security-scan-branches != '[]' && needs.get-branches-to-scan.outputs.security-scan-branches != '' | |
| environment: security-scanning-workflow-env | |
| permissions: | |
| id-token: write # Required for OIDC | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: [code-editor-sagemaker-server] | |
| branch: ${{ fromJson(needs.get-branches-to-scan.outputs.security-scan-branches) }} | |
| steps: | |
| - name: Assume IAM Role | |
| id: assume-aws-iam-role | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| role-duration-seconds: 900 | |
| aws-region: us-east-1 | |
| role-session-name: scan-${{ matrix.target }}-${{matrix.branch}} | |
| - name: Publish Scan Invoked metric | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| TARGET: ${{ matrix.target }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "SecurityScanInvoked" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=SecurityScan,Target=$TARGET,Branch=$BRANCH" \ | |
| --value 1 | |
| - name: Checkout branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ matrix.branch }} | |
| submodules: recursive | |
| - name: Update scripts from main branch | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPOSITORY: ${{ github.repository }} | |
| run: | | |
| # Older branches may not have the latest versions of the | |
| # security scan scripts. So we download the latest scripts folder from main | |
| echo "Downloading latest scripts folder from main branch" | |
| cd scripts | |
| rm -f *.sh | |
| gh api "/repos/$REPOSITORY/contents/scripts?ref=main" --jq '.[] | select(.type == "file" and (.name | endswith(".sh"))) | .name' | \ | |
| while read filename; do | |
| echo "Downloading: $filename" | |
| raw_url="https://raw.githubusercontent.com/$REPOSITORY/main/scripts/$filename" | |
| curl -sSL "$raw_url" -o "$filename" | |
| if [ -s "$filename" ]; then | |
| echo "✓ Successfully downloaded: $filename ($(wc -c < "$filename") bytes)" | |
| else | |
| echo "✗ Failed to download: $filename" | |
| exit 1 | |
| fi | |
| done | |
| sudo chmod +x *.sh | |
| echo "Updated scripts folder to latest version from main" | |
| - name: Set up environment | |
| run: | | |
| echo "Installing required dependencies" | |
| sudo apt-get update | |
| sudo apt-get install -y quilt libkrb5-dev libx11-dev libxkbfile-dev libxml2-utils | |
| - name: Run patches script | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| run: | | |
| ./scripts/prepare-src.sh "$TARGET" | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '22' | |
| cache: 'npm' | |
| cache-dependency-path: 'code-editor-src/package-lock.json' | |
| - name: Install Code Editor Dependencies | |
| run: | | |
| cd code-editor-src | |
| echo "Installing Dependencies" | |
| npm ci | |
| - name: Install Security Scan Dependencies | |
| run: | | |
| echo "Installing CycloneDX SBOM for npm" | |
| npm i -g @cyclonedx/cyclonedx-npm | |
| - name: Run Security Scan | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| ./scripts/security-scan.sh scan-main-dependencies "$TARGET" "$BRANCH" | |
| - name: Upload SBOM Files | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: sbom-files-${{ matrix.target }}-${{ matrix.branch }} | |
| path: | | |
| code-editor-src/*-sbom.json | |
| code-editor-src/remote/*-sbom.json | |
| code-editor-src/extensions/*-sbom.json | |
| code-editor-src/remote/web/*-sbom.json | |
| retention-days: 90 | |
| if-no-files-found: error | |
| - name: Upload Scan Result Files | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: scan-results-${{ matrix.target }}-${{ matrix.branch }} | |
| path: | | |
| code-editor-src/*-scan-result.json | |
| code-editor-src/remote/*-scan-result.json | |
| code-editor-src/extensions/*-scan-result.json | |
| code-editor-src/remote/web/*-scan-result.json | |
| retention-days: 90 | |
| if-no-files-found: error | |
| - name: Analyze SBOM Scan Results | |
| env: | |
| TARGET: ${{ matrix.target }} | |
| run: | | |
| ./scripts/security-scan.sh analyze-results "$TARGET" "scan_results_paths.txt" | |
| - name: Create Success Indicator File | |
| env: | |
| MATRIX_BRANCH: ${{ matrix.branch }} | |
| TARGET: ${{ matrix.target }} | |
| run: | | |
| output_branch="$MATRIX_BRANCH" | |
| echo "PASS" > "scan-success-$TARGET-${output_branch}.txt" | |
| - name: Upload Success Indicator File | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: scan-success-${{ matrix.target }}-${{ matrix.branch }} | |
| path: scan-success-${{ matrix.target }}-${{ matrix.branch }}.txt | |
| retention-days: 90 | |
| - name: Publish Scan Successful Metric | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| TARGET: ${{ matrix.target }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "SecurityScanSuccessful" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=SecurityScan,Target=$TARGET,Branch=$BRANCH" \ | |
| --value 1 | |
| - name: Publish Failure Metrics | |
| if: failure() && github.event_name != 'workflow_dispatch' | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| TARGET: ${{ matrix.target }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| echo "Job failed - publishing failure metrics" | |
| # Publish workflow failure metric | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "SecurityScanFailed" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=SecurityScan,Target=$TARGET,Branch=$BRANCH" \ | |
| --value 1 | |
| generate-security-scan-output: | |
| runs-on: ubuntu-latest | |
| needs: [get-branches-to-scan, security-scan] | |
| if: always() && needs.get-branches-to-scan.outputs.security-scan-branches != '[]' && needs.get-branches-to-scan.outputs.security-scan-branches != '' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| branch: ${{ fromJson(needs.get-branches-to-scan.outputs.security-scan-branches) }} | |
| steps: | |
| - name: Download all scan success files | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: scan-success-* | |
| merge-multiple: true | |
| - name: Check if branch was successful for all targets | |
| env: | |
| MATRIX_BRANCH: ${{ matrix.branch }} | |
| run: | | |
| # Parse targets from environment variable | |
| readarray -t targets < <(jq -r '.[]' <<< "$CODE_EDITOR_TARGETS") | |
| check_branch="$MATRIX_BRANCH" | |
| all_success=true | |
| echo "Checking success for branch: $check_branch (matrix branch: $MATRIX_BRANCH)" | |
| echo "Targets to check:" "${targets[@]}" | |
| # Check if all target success files exist for this branch | |
| for target in "${targets[@]}"; do | |
| success_file="scan-success-${target}-${check_branch}.txt" | |
| echo "Checking for file: $success_file" | |
| if [ -f "$success_file" ]; then | |
| echo "✓ Found success file for target $target on branch $check_branch" | |
| else | |
| echo "✗ Missing success file for target $target on branch $check_branch" | |
| all_success=false | |
| break | |
| fi | |
| done | |
| # Create branch success file only if all targets succeeded | |
| if [ "$all_success" = true ]; then | |
| echo "✓ All scans successful for branch $check_branch - creating branch success file" | |
| echo "PASS" > "scan-success-branch-${check_branch}.txt" | |
| else | |
| echo "✗ Some scans failed for branch $check_branch - not creating branch success file" | |
| exit 1 | |
| fi | |
| - name: Upload Branch Success File | |
| if: success() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: scan-success-branch-${{ matrix.branch }} | |
| path: scan-success-branch-${{ matrix.branch }}.txt | |
| retention-days: 90 | |
| security-scan-global-dependencies: | |
| runs-on: ubuntu-latest | |
| needs: [get-branches-to-scan] | |
| if: needs.get-branches-to-scan.outputs.global-dependencies-branches != '[]' && needs.get-branches-to-scan.outputs.global-dependencies-branches != '' | |
| environment: security-scanning-workflow-env | |
| permissions: | |
| id-token: write # Required for OIDC | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| branch: ${{ fromJson(needs.get-branches-to-scan.outputs.global-dependencies-branches) }} | |
| steps: | |
| - name: Assume IAM Role | |
| id: assume-aws-iam-role | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| aws-region: us-east-1 | |
| role-session-name: scan-global-dependencies-${{matrix.branch}} | |
| role-duration-seconds: 900 | |
| - name: Publish Scan Invoked metric | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "GlobalDependenciesSecurityScanInvoked" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=GlobalDependenciesSecurityScan,Branch=$BRANCH" \ | |
| --value 1 | |
| - name: Checkout branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ matrix.branch }} | |
| submodules: recursive | |
| - name: Update scripts from main branch | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| REPOSITORY: ${{ github.repository }} | |
| run: | | |
| # Older branches may not have the latest versions of the | |
| # security scan scripts. So we download the latest one from main | |
| echo "Downloading latest security-scan.sh script from main branch" | |
| cd scripts | |
| rm -f *.sh | |
| gh api "/repos/$REPOSITORY/contents/scripts?ref=main" --jq '.[] | select(.type == "file" and (.name | endswith(".sh"))) | .name' | \ | |
| while read filename; do | |
| echo "Downloading: $filename" | |
| raw_url="https://raw.githubusercontent.com/$REPOSITORY/main/scripts/$filename" | |
| curl -sSL "$raw_url" -o "$filename" | |
| if [ -s "$filename" ]; then | |
| echo "✓ Successfully downloaded: $filename ($(wc -c < "$filename") bytes)" | |
| else | |
| echo "✗ Failed to download: $filename" | |
| exit 1 | |
| fi | |
| done | |
| sudo chmod +x *.sh | |
| echo "Updated security-scan.sh to latest version from main" | |
| - name: Install Security Scan Dependencies | |
| run: | | |
| echo "Installing CycloneDX SBOM for npm" | |
| npm i -g @cyclonedx/cyclonedx-npm | |
| echo "Installing OSS Attribution Generator" | |
| source .packageversionrc | |
| npm i -g @electrovir/oss-attribution-generator@$oss_attribution_generator_version | |
| echo "Installing semver" | |
| npm i -g semver@$semver_version | |
| echo "Installing Syft for SBOM generation" | |
| curl -sSfL https://get.anchore.io/syft | sudo sh -s -- -b /usr/local/bin | |
| echo "Syft installation completed" | |
| syft version | |
| - name: Prepare Additional Node JS Dependencies for Scanning | |
| run: | | |
| ./scripts/security-scan.sh scan-additional-dependencies | |
| - name: Upload Additional Node.js SBOMs | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: additional-nodejs-sboms-${{ matrix.branch }} | |
| path: additional-node-js-sboms/ | |
| retention-days: 90 | |
| if-no-files-found: error | |
| - name: Upload Additional Inspector Scan Results | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: additional-inspector-results-${{ matrix.branch }} | |
| path: additional-scan-results/ | |
| retention-days: 90 | |
| if-no-files-found: error | |
| - name: Analyze Additional SBOM Scan Results | |
| run: | | |
| ./scripts/security-scan.sh analyze-results "Global Dependencies" "additional_scan_results_paths.txt" | |
| - name: Scan GitHub Security Advisories | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| ./scripts/security-scan.sh scan-github-advisories | |
| - name: Create Global Success Indicator File | |
| env: | |
| MATRIX_BRANCH: ${{ matrix.branch }} | |
| run: | | |
| output_branch="$MATRIX_BRANCH" | |
| echo "PASS" > "global-scan-success-${output_branch}.txt" | |
| - name: Upload Global Success Indicator File | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: global-scan-success-${{ matrix.branch }} | |
| path: global-scan-success-${{ matrix.branch }}.txt | |
| retention-days: 90 | |
| - name: Publish Failure Metrics | |
| if: failure() && github.event_name != 'workflow_dispatch' | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| BRANCH: ${{ matrix.branch }} | |
| run: | | |
| echo "Job failed - publishing failure metrics" | |
| # Publish workflow failure metric | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "SecurityScanFailed" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=GlobalDependenciesSecurityScan,Branch=$BRANCH" \ | |
| --value 1 | |
| invoke-build-workflow: | |
| name: Invoke Build Workflow | |
| runs-on: ubuntu-latest | |
| needs: [generate-security-scan-output, security-scan-global-dependencies] | |
| if: success() && (github.event_name != 'workflow_dispatch' || !inputs.standalone-run) && github.event_name != 'schedule' | |
| permissions: | |
| actions: write # Required for workflow invocation | |
| contents: read | |
| steps: | |
| - name: Trigger Build Workflow | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| github.rest.actions.createWorkflowDispatch({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| workflow_id: "build-targets.yaml", | |
| ref: context.ref | |
| }) | |
| handle-failures: | |
| name: Handle Failures | |
| runs-on: ubuntu-latest | |
| needs: [generate-security-scan-output] | |
| environment: security-scanning-workflow-env | |
| if: failure() && github.event_name != 'workflow_dispatch' | |
| permissions: | |
| id-token: write # Required for OIDC | |
| env: | |
| REPOSITORY: ${{ github.repository }} | |
| AWS_ROLE_TO_ASSUME: ${{ secrets.AWS_ROLE_TO_ASSUME }} | |
| steps: | |
| - name: Use role credentials for metrics | |
| id: aws-creds | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ env.AWS_ROLE_TO_ASSUME }} | |
| role-duration-seconds: 900 | |
| aws-region: us-east-1 | |
| - name: Report failure | |
| run: | | |
| aws cloudwatch put-metric-data \ | |
| --namespace "GitHub/Workflows" \ | |
| --metric-name "ExecutionsFailed" \ | |
| --dimensions "Repository=$REPOSITORY,Workflow=SecurityScan" \ | |
| --value 1 |