container scanning #11
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: DevSecOps Pipeline | |
| on: | |
| push: | |
| branches: [ main, master, develop ] | |
| pull_request: | |
| branches: [ main, master, develop ] | |
| workflow_dispatch: | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository_owner }}/solar-system | |
| DOCKER_IMAGE_TAG: ${{ github.sha }} | |
| jobs: | |
| # Stage 1 & 2: Code Checkout and Dependency Installation | |
| build-and-test: | |
| name: Build and Unit Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Run unit tests | |
| run: npm test | |
| continue-on-error: false # Fail pipeline if tests fail | |
| env: | |
| MONGO_URI: ${{ secrets.MONGO_URI }} | |
| - name: Upload test results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: test-results | |
| path: test-results.xml | |
| # Stage 4: Code Coverage | |
| code-coverage: | |
| name: Code Coverage Analysis | |
| runs-on: ubuntu-latest | |
| needs: build-and-test | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Run coverage analysis | |
| run: npm run coverage | |
| env: | |
| MONGO_URI: ${{ secrets.MONGO_URI }} | |
| - name: Upload coverage reports | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: coverage-reports | |
| path: | | |
| coverage/ | |
| .nyc_output/ | |
| # Stage 5: SAST - Static Application Security Testing | |
| sast-semgrep: | |
| name: SAST - Semgrep | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Run Semgrep | |
| uses: returntocorp/semgrep-action@v1 | |
| with: | |
| config: >- | |
| p/security-audit | |
| p/nodejs | |
| p/owasp-top-ten | |
| p/javascript | |
| - name: Upload Semgrep results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: semgrep-results | |
| path: semgrep-results.json | |
| # Stage 6: Dependency Scanning | |
| dependency-scan: | |
| name: Dependency Scanning - Snyk | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '18' | |
| - name: Install dependencies | |
| run: npm install | |
| - name: Run Snyk to check for vulnerabilities | |
| uses: snyk/actions/node@master | |
| continue-on-error: true | |
| env: | |
| SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} | |
| with: | |
| args: --severity-threshold=high --json-file-output=snyk-results.json | |
| - name: Upload Snyk results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: snyk-results | |
| path: snyk-results.json | |
| - name: Run npm audit | |
| run: npm audit --json > npm-audit-results.json | |
| continue-on-error: true | |
| - name: Upload npm audit results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: npm-audit-results | |
| path: npm-audit-results.json | |
| # Stage 7: Secret Detection | |
| secret-scan: | |
| name: Secret Detection - TruffleHog | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Full history for secret scanning | |
| - name: TruffleHog OSS | |
| uses: trufflesecurity/trufflehog@main | |
| continue-on-error: true # Don't fail pipeline on secrets found | |
| with: | |
| path: ./ | |
| base: ${{ github.event.before || '' }} | |
| head: ${{ github.sha }} | |
| extra_args: --only-verified | |
| # Stage 8: Docker Build and Push | |
| docker-build: | |
| name: Docker Build and Push | |
| runs-on: ubuntu-latest | |
| needs: [build-and-test, code-coverage, sast-semgrep, dependency-scan, secret-scan] | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata for Docker | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=sha,prefix={{branch}}- | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| push: true | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Save image name for later stages | |
| run: | | |
| echo "FULL_IMAGE_NAME=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ github.sha }}" >> $GITHUB_ENV | |
| echo "Image pushed: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ github.sha }}" | |
| # Stage 9: Container Scanning | |
| container-scan: | |
| name: Container Scanning - Trivy | |
| runs-on: ubuntu-latest | |
| needs: docker-build | |
| permissions: | |
| contents: read | |
| packages: read | |
| security-events: write | |
| steps: | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} | |
| format: 'sarif' | |
| output: 'trivy-results.sarif' | |
| severity: 'CRITICAL,HIGH,MEDIUM' | |
| - name: Upload Trivy results to GitHub Security | |
| uses: github/codeql-action/upload-sarif@v3 | |
| if: always() | |
| with: | |
| sarif_file: 'trivy-results.sarif' | |
| - name: Run Trivy for JSON output | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} | |
| format: 'json' | |
| output: 'trivy-results.json' | |
| - name: Upload Trivy results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: trivy-results | |
| path: trivy-results.json | |
| # Stage 10: DAST - Dynamic Application Security Testing | |
| dast-zap: | |
| name: DAST - OWASP ZAP | |
| runs-on: ubuntu-latest | |
| needs: docker-build | |
| permissions: | |
| contents: read | |
| packages: read | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Start application container | |
| run: | | |
| docker run -d --name solar-system-app \ | |
| -p 3000:3000 \ | |
| -e MONGO_URI="${{ secrets.MONGO_URI }}" \ | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }} | |
| # Wait for application to be ready | |
| sleep 10 | |
| curl -f http://localhost:3000/ready || exit 1 | |
| - name: Run OWASP ZAP Baseline Scan | |
| uses: zaproxy/action-baseline@v0.12.0 | |
| with: | |
| target: 'http://localhost:3000/' | |
| rules_file_name: '.zap/rules.tsv' | |
| cmd_options: '-a' | |
| allow_issue_writing: false | |
| - name: Stop application container | |
| if: always() | |
| run: docker stop solar-system-app && docker rm solar-system-app | |
| # Stage 11: Deploy to Azure VM | |
| # deploy-azure: | |
| # name: Deploy to Azure VM | |
| # runs-on: ubuntu-latest | |
| # needs: [container-scan, dast-zap] | |
| # if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' | |
| # permissions: | |
| # contents: read | |
| # packages: read | |
| # environment: | |
| # name: production | |
| # url: http://${{ secrets.AZURE_VM_IP }}:3000 | |
| # steps: | |
| # - name: Deploy on Azure VM | |
| # uses: appleboy/ssh-action@master | |
| # with: | |
| # host: ${{ secrets.AZURE_VM_IP }} | |
| # username: ${{ secrets.AZURE_VM_USERNAME }} | |
| # key: ${{ secrets.AZURE_VM_SSH_KEY }} | |
| # script: | | |
| # # Log in to GitHub Container Registry | |
| # echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin | |
| # # Pull the latest image | |
| # docker pull ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ github.sha }} | |
| # # Stop and remove old container if exists | |
| # docker stop solar-system || true | |
| # docker rm solar-system || true | |
| # # Run new container | |
| # docker run -d --name solar-system \ | |
| # -p 3000:3000 \ | |
| # --restart unless-stopped \ | |
| # -e MONGO_URI="${{ secrets.MONGO_URI }}" \ | |
| # ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}-${{ github.sha }} | |
| # # Clean up old images (keep last 3) | |
| # docker image prune -af --filter "until=72h" | |
| # # Verify deployment | |
| # sleep 5 | |
| # curl -f http://localhost:3000/ready || exit 1 | |
| # echo "Deployment successful!" | |
| # docker ps | grep solar-system | |
| # - name: Health check | |
| # run: | | |
| # sleep 10 | |
| # curl -f http://${{ secrets.AZURE_VM_IP }}:3000/ready || exit 1 | |
| # echo "Application is healthy and running!" |