Skip to content

deploy stage added

deploy stage added #16

name: DevSecOps Pipeline

Check failure on line 1 in .github/workflows/devsecops-pipeline.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/devsecops-pipeline.yml

Invalid workflow file

(Line: 352, Col: 12): Unrecognized named-value: 'secrets'. Located at position 1 within expression: secrets.AZURE_WEBAPP_NAME
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: Generate Snyk HTML report
if: always()
continue-on-error: true
run: |
npx snyk test --severity-threshold=high > snyk-results.txt 2>&1 || true
npx snyk-to-html -i snyk-results.json -o snyk-results.html || echo "HTML generation skipped"
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
- name: Upload Snyk results
if: always()
uses: actions/upload-artifact@v4
with:
name: snyk-results
path: |
snyk-results.json
snyk-results.html
snyk-results.txt
- 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: Run Trivy for HTML output
uses: aquasecurity/trivy-action@master
continue-on-error: true
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
format: 'template'
template: '@$HOME/.local/bin/trivy-bin/contrib/html.tpl'
output: 'trivy-results.html'
- name: Upload Trivy results
if: always()
uses: actions/upload-artifact@v4
with:
name: trivy-results
path: |
trivy-results.json
trivy-results.html
# 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/'
cmd_options: '-a'
allow_issue_writing: false
artifact_name: '' # Disable ZAP's internal artifact upload (we handle it manually)
continue-on-error: true # Don't fail pipeline on warnings
- name: Upload ZAP scan results
if: always()
uses: actions/upload-artifact@v4
with:
name: zap-dast-results
path: |
report_json.json
report_md.md
report_html.html
- name: Stop application container
if: always()
run: docker stop solar-system-app && docker rm solar-system-app
# Stage 11: Deploy Infrastructure and Application to Azure
deploy-azure:
name: Deploy to Azure Web App
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: https://${{ secrets.AZURE_WEBAPP_NAME }}.azurewebsites.net
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Init
run: terraform init
working-directory: ./terraform
- name: Terraform Plan
run: |
terraform plan \
-var="app_name=${{ secrets.AZURE_WEBAPP_NAME }}" \
-var="github_username=${{ github.repository_owner }}" \
-var="github_token=${{ secrets.GITHUB_TOKEN }}" \
-var="mongo_uri=${{ secrets.MONGO_URI }}" \
-var="docker_image=${{ github.repository_owner }}/solar-system:${{ github.ref_name }}" \
-out=tfplan
working-directory: ./terraform
env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
- name: Terraform Apply
run: terraform apply -auto-approve tfplan
working-directory: ./terraform
env:
ARM_CLIENT_ID: ${{ secrets.ARM_CLIENT_ID }}
ARM_CLIENT_SECRET: ${{ secrets.ARM_CLIENT_SECRET }}
ARM_SUBSCRIPTION_ID: ${{ secrets.ARM_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.ARM_TENANT_ID }}
- name: Get Web App URL
id: webapp
run: |
WEBAPP_URL=$(terraform output -raw webapp_url)
echo "url=$WEBAPP_URL" >> $GITHUB_OUTPUT
working-directory: ./terraform
- name: Restart Web App (force pull latest image)
run: |
az webapp restart \
--name ${{ secrets.AZURE_WEBAPP_NAME }} \
--resource-group $(terraform output -raw resource_group_name)
working-directory: ./terraform
- name: Health check
run: |
sleep 60
curl -f ${{ steps.webapp.outputs.url }}/ready || exit 1
echo "Application is healthy and running on Azure Web App!"