diff --git a/.github/workflows/azure-static-web-apps-black-smoke-0e2f99000.yml b/.github/workflows/azure-static-web-apps-black-smoke-0e2f99000.yml new file mode 100644 index 0000000..82f09e6 --- /dev/null +++ b/.github/workflows/azure-static-web-apps-black-smoke-0e2f99000.yml @@ -0,0 +1,58 @@ +name: Azure Static Web Apps CI/CD + +on: + push: + branches: + - develop + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - develop + +jobs: + build_and_deploy_job: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + name: Build and Deploy Job + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@v3 + with: + submodules: true + lfs: false + - name: Install OIDC Client from Core Package + run: npm install @actions/core@1.6.0 @actions/http-client + - name: Get Id Token + uses: actions/github-script@v6 + id: idtoken + with: + script: | + const coredemo = require('@actions/core') + return await coredemo.getIDToken() + result-encoding: string + - name: Build And Deploy + id: builddeploy + uses: Azure/static-web-apps-deploy@v1 + with: + azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_BLACK_SMOKE_0E2F99000 }} + action: "upload" + ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### + # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig + app_location: "./react-client" # App source code path + api_location: "" # Api source code path - optional + output_location: "build" # Built app content directory - optional + github_id_token: ${{ steps.idtoken.outputs.result }} + ###### End of Repository/Build Configurations ###### + + close_pull_request_job: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + name: Close Pull Request Job + steps: + - name: Close Pull Request + id: closepullrequest + uses: Azure/static-web-apps-deploy@v1 + with: + action: "close" diff --git a/.github/workflows/sast.yml b/.github/workflows/sast.yml new file mode 100644 index 0000000..8392ac2 --- /dev/null +++ b/.github/workflows/sast.yml @@ -0,0 +1,123 @@ +name: Secure CI/CD Production Pipeline + +on: + push: + branches: [ "develop" ] + pull_request: + branches: [ "develop" ] + +jobs: + sast_scan: + name: 1. Vulnerability Assessment (SAST) + runs-on: ubuntu-latest + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run SonarQube Scan Engine + uses: SonarSource/sonarqube-scan-action@v3 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + with: + args: > + -Dsonar.projectKey=TaskApp-DevSecOps + -Dsonar.projectName=TaskApp + -Dsonar.sources=. + + - name: Enforce Custom Quality Gate Status + uses: SonarSource/sonarqube-quality-gate-action@v1.1.0 + timeout-minutes: 5 + env: + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }} + + sca_scan: + name: 2. Open-Source Dependency Scan (SCA) + runs-on: ubuntu-latest + needs: sast_scan + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + + - name: Snyk - Scan Project Dependencies + uses: snyk/actions/node@master + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + with: + args: --severity-threshold=high --file=package.json + + container_scan: + name: 3. Container Image Security Scan + runs-on: ubuntu-latest + needs: sca_scan + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + + - name: Build Staging Docker Image + run: | + docker build -t taskapp-backend:staging . + + - name: Run Trivy Security Vulnerability Scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'taskapp-backend:staging' + format: 'table' + exit-code: '1' + ignore-unfixed: true + vuln-type: 'os,library' + severity: 'CRITICAL,HIGH' + + deploy_staging: + name: 4. Staging Environment Deployment + runs-on: ubuntu-latest + needs: container_scan + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + + - name: Spin Up Application for Security Probe + run: | + echo "All structural gates passed. Launching staging environment instances..." + echo "Staging deployment active at http://20.187.120.80:9000 (Simulated)" + + dast_scan: + name: 5. Dynamic Application Security Testing (DAST) + runs-on: ubuntu-latest + needs: deploy_staging # CRITICAL: Ensures the app is "live" before scanning! + steps: + - name: Checkout Source Code + uses: actions/checkout@v4 + + - name: Create Report Directory + run: mkdir -p ${{ github.workspace }}/zap + + # Step 1 & 2: Pull and Run ZAP Baseline scan against your live frontend target URL + - name: Run ZAP Baseline Scan (Frontend) + run: | + docker run --user root -v ${{ github.workspace }}/zap:/zap/wrk/:rw \ + zaproxy/zap-stable zap-baseline.py \ + -t http://20.187.120.80:9000 \ + -r zap-report.html \ + -I + + # Step 3: Run ZAP API scan against your backend routing schema definitions + - name: Run ZAP API Scan (Backend) + run: | + docker run --user root -v ${{ github.workspace }}/zap:/zap/wrk/:rw \ + zaproxy/zap-stable zap-api-scan.py \ + -t http://20.187.120.80:9000/api/openapi.json \ + -f openapi \ + -r zap-api-report.html \ + -I + + # Step 4: Publish HTML Security Reports as downloadable pipeline artifacts + - name: Publish ZAP Security Report Artifacts + uses: actions/upload-artifact@v4 + if: always() # Guarantees reports publish even if structural security vulnerabilities match + with: + name: zap-security-reports + path: ${{ github.workspace }}/zap/ diff --git a/node-express-server/.dockerignore b/node-express-server/.dockerignore new file mode 100644 index 0000000..21c6f1a --- /dev/null +++ b/node-express-server/.dockerignore @@ -0,0 +1,4 @@ +node_modules +npm-debug.log +.git +.env \ No newline at end of file diff --git a/node-express-server/Dockerfile b/node-express-server/Dockerfile new file mode 100644 index 0000000..f394abb --- /dev/null +++ b/node-express-server/Dockerfile @@ -0,0 +1,13 @@ +# Multi-stage build for a smaller, secure image +FROM node:18-alpine AS builder +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only-production +COPY . . + +FROM node:18-alpine +WORKDIR /app +COPY --from-builder /app . +EXPOSE 8080 +# Note: Based on your screenshot, the main file is server.js +CMD ["node", "server.js"] \ No newline at end of file diff --git a/node-express-server/app/config/db.config.js b/node-express-server/app/config/db.config.js index 5fa11de..3f0be58 100644 --- a/node-express-server/app/config/db.config.js +++ b/node-express-server/app/config/db.config.js @@ -1,9 +1,15 @@ module.exports = { - HOST: "localhost", - USER: "root", - PASSWORD: "123456", - DB: "testdb", - dialect: "mysql", + HOST: "mysql-taskapp-reeha.mysql.database.azure.com", // This is your server building name + USER: "dbuser", + PASSWORD: "Devops@123456789", + DB: "flexibleserverdb", // <-- CHANGE THIS BACK TO Azure's default database room! + dialect: "mysql", + dialectOptions: { + ssl: { + require: true, + rejectUnauthorized: false + } + }, pool: { max: 5, min: 0, diff --git a/node-express-server/server.js b/node-express-server/server.js index 54c7e21..2932b57 100644 --- a/node-express-server/server.js +++ b/node-express-server/server.js @@ -1,6 +1,7 @@ const express = require("express"); const cors = require("cors"); + const app = express(); var corsOptions = { diff --git a/react-client/Dockerfile b/react-client/Dockerfile new file mode 100644 index 0000000..48fe00a --- /dev/null +++ b/react-client/Dockerfile @@ -0,0 +1,19 @@ +# 1. Build Stage +FROM node:16-alpine as build +WORKDIR /app +COPY package*.json ./ +RUN npm install +COPY . . + +# --- CRUCIAL: THESE TWO LINES MUST BE RIGHT HERE (BEFORE THE BUILD RUN) --- +ARG REACT_APP_API_URL +ENV REACT_APP_API_URL=$REACT_APP_API_URL + +RUN npm run build + +# 2. Production Stage (Nginx) +FROM nginx:alpine +COPY --from=build /app/build /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/react-client/nginx.conf b/react-client/nginx.conf new file mode 100644 index 0000000..333bc49 --- /dev/null +++ b/react-client/nginx.conf @@ -0,0 +1,8 @@ +server { + listen 80; + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } +} \ No newline at end of file diff --git a/react-client/src/http-common.js b/react-client/src/http-common.js index 5b11f3e..554bcbc 100644 --- a/react-client/src/http-common.js +++ b/react-client/src/http-common.js @@ -1,8 +1,9 @@ import axios from "axios"; export default axios.create({ - baseURL: "http://localhost:8080/api", + // Make sure this points to the BACKEND domain, followed by /api + baseURL: "https://app-taskapp-backend-reeha-fnd0b7b9dqcpazh8.eastasia-01.azurewebsites.net/api", headers: { "Content-type": "application/json" } -}); \ No newline at end of file +});