From 7af7f8a1c451f5797564001f7a91ef2cca38995f Mon Sep 17 00:00:00 2001 From: Nandan P Aghera Date: Mon, 9 Mar 2026 11:01:41 +0530 Subject: [PATCH 01/43] Add complete DevSecOps pipeline + UI improvements --- .github/workflows/code-quality.yml | 29 +++++ .github/workflows/dependency-scan.yml | 26 ++++ .github/workflows/deploy-to-server.yml | 55 ++++++++ .github/workflows/devsecops-pipeline.yml | 46 +++++++ .github/workflows/docker-build-push.yml | 29 +++++ .github/workflows/docker-lint.yml | 19 +++ .github/workflows/image-scan.yml | 28 +++++ .github/workflows/secrets-scan.yml | 20 +++ .trivyignore | 4 + Dockerfile | 24 +++- docker-compose.yml | 10 +- pom.xml | 24 ++++ src/main/resources/static/css/bankapp.css | 119 ++++++++++++++++++ src/main/resources/templates/dashboard.html | 6 + .../resources/templates/fragments/layout.html | 17 ++- src/main/resources/templates/login.html | 3 + src/main/resources/templates/register.html | 3 + 17 files changed, 451 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/code-quality.yml create mode 100644 .github/workflows/dependency-scan.yml create mode 100644 .github/workflows/deploy-to-server.yml create mode 100644 .github/workflows/devsecops-pipeline.yml create mode 100644 .github/workflows/docker-build-push.yml create mode 100644 .github/workflows/docker-lint.yml create mode 100644 .github/workflows/image-scan.yml create mode 100644 .github/workflows/secrets-scan.yml create mode 100644 .trivyignore diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml new file mode 100644 index 0000000..84f2e38 --- /dev/null +++ b/.github/workflows/code-quality.yml @@ -0,0 +1,29 @@ +# Linting and SAST scan for Java +# Lint: Checkstyle (via Maven) | SAST: SpotBugs (via Maven) + +name: Code Quality + +on: + workflow_call: + + +jobs: + validate: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Run Checkstyle (Lint) + run: mvn checkstyle:check + + - name: Run SpotBugs (SAST) + run: mvn spotbugs:check diff --git a/.github/workflows/dependency-scan.yml b/.github/workflows/dependency-scan.yml new file mode 100644 index 0000000..22f3d7e --- /dev/null +++ b/.github/workflows/dependency-scan.yml @@ -0,0 +1,26 @@ +# Scan Maven dependencies for known CVEs +# Uses OWASP Dependency-Check (Java equivalent of pip-audit) + +name: Dependency scan + +on: + workflow_call: + + +jobs: + dependency-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Java 21 + uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: 'temurin' + cache: maven + + - name: Run OWASP Dependency-Check + run: mvn org.owasp:dependency-check-maven:check -DfailBuildOnCVSS=7 diff --git a/.github/workflows/deploy-to-server.yml b/.github/workflows/deploy-to-server.yml new file mode 100644 index 0000000..2d8e339 --- /dev/null +++ b/.github/workflows/deploy-to-server.yml @@ -0,0 +1,55 @@ +# Deploy the safe / secure / tested image to prod server +name: Deploy to Server + +on: + workflow_call: + + +jobs: + + deploy: + + env: + DOCKERHUB_USER: ${{ vars.DOCKERHUB_USER }} + DOCKER_TAG: ${{ github.sha }} + + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: SSH to prod server — install Docker + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.EC2_SSH_HOST }} + username: ${{ secrets.EC2_SSH_USER }} + key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} + script: | + sudo apt-get update && sudo apt-get install -y docker.io docker-compose-v2 + sudo usermod -aG docker $USER + mkdir -p ~/devops + + - name: Copy docker-compose file to server + uses: appleboy/scp-action@v1 + with: + host: ${{ secrets.EC2_SSH_HOST }} + username: ${{ secrets.EC2_SSH_USER }} + key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} + source: docker-compose.yml + target: ~/devops + + - name: SSH to prod server — run the app + uses: appleboy/ssh-action@v1.0.3 + with: + host: ${{ secrets.EC2_SSH_HOST }} + username: ${{ secrets.EC2_SSH_USER }} + key: ${{ secrets.EC2_SSH_PRIVATE_KEY }} + script: | + export DOCKERHUB_USER=${{ vars.DOCKERHUB_USER }} + export DOCKER_TAG=${{ github.sha }} + export DB_USERNAME=${{ secrets.DB_USERNAME }} + export DB_PASSWORD=${{ secrets.DB_PASSWORD }} + export DB_ROOT_PASSWORD=${{ secrets.DB_ROOT_PASSWORD }} + echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ vars.DOCKERHUB_USER }} --password-stdin + cd ~/devops && docker compose down && docker compose pull && docker compose up -d --force-recreate diff --git a/.github/workflows/devsecops-pipeline.yml b/.github/workflows/devsecops-pipeline.yml new file mode 100644 index 0000000..e52cfbf --- /dev/null +++ b/.github/workflows/devsecops-pipeline.yml @@ -0,0 +1,46 @@ +name: DevSecOps end-to-end pipeline + +on: + workflow_dispatch: + + +jobs: + + # ── CI Security Scans ──────────────────────────────────────────── + + code-quality: + uses: ./.github/workflows/code-quality.yml + + secrets-scan: + uses: ./.github/workflows/secrets-scan.yml + secrets: inherit + + dependency-scan: + uses: ./.github/workflows/dependency-scan.yml + + docker-scan: + uses: ./.github/workflows/docker-lint.yml + + + # ── Build — only after all scans pass ─────────────────────────── + + build: + needs: [code-quality, secrets-scan, dependency-scan, docker-scan] + uses: ./.github/workflows/docker-build-push.yml + secrets: inherit + + + # ── Image scan — scan the freshly built image with Trivy ───────── + + trivy: + needs: [build] + uses: ./.github/workflows/image-scan.yml + secrets: inherit + + + # ── Deploy — to prod server only after image is verified clean ─── + + deploy: + needs: [trivy] + uses: ./.github/workflows/deploy-to-server.yml + secrets: inherit diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml new file mode 100644 index 0000000..d25759e --- /dev/null +++ b/.github/workflows/docker-build-push.yml @@ -0,0 +1,29 @@ +name: Docker build & push + +on: + workflow_call: + + +jobs: + build-and-push: + runs-on: ubuntu-latest + + steps: + - name: Code checkout + uses: actions/checkout@v4 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and Push to Docker Hub + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: | + ${{ vars.DOCKERHUB_USER }}/bankapp:${{ github.ref_name }} + ${{ vars.DOCKERHUB_USER }}/bankapp:latest + ${{ vars.DOCKERHUB_USER }}/bankapp:${{ github.sha }} diff --git a/.github/workflows/docker-lint.yml b/.github/workflows/docker-lint.yml new file mode 100644 index 0000000..600b9f8 --- /dev/null +++ b/.github/workflows/docker-lint.yml @@ -0,0 +1,19 @@ +# Scan the Dockerfile for issues and best-practice violations +name: Docker lint + +on: + workflow_call: + + +jobs: + validate-dockerfile: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate Dockerfile + uses: hadolint/hadolint-action@v3.1.0 + with: + dockerfile: Dockerfile diff --git a/.github/workflows/image-scan.yml b/.github/workflows/image-scan.yml new file mode 100644 index 0000000..1de2c0b --- /dev/null +++ b/.github/workflows/image-scan.yml @@ -0,0 +1,28 @@ +# Uses Trivy image scanner for CVEs — scans the built Docker image +name: Image Scanner + +on: + workflow_call: + + +jobs: + image-scanner: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to DockerHub + uses: docker/login-action@v3 + with: + username: ${{ vars.DOCKERHUB_USER }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Trivy scanner + uses: aquasecurity/trivy-action@0.35.0 + with: + image-ref: ${{ vars.DOCKERHUB_USER }}/bankapp:${{ github.sha }} + severity: 'CRITICAL,HIGH' + exit-code: '1' + trivyignore: ${{ github.workspace }}/.trivyignore diff --git a/.github/workflows/secrets-scan.yml b/.github/workflows/secrets-scan.yml new file mode 100644 index 0000000..eae0dd2 --- /dev/null +++ b/.github/workflows/secrets-scan.yml @@ -0,0 +1,20 @@ +# API keys, sensitive data, credentials +# we use Gitleaks for this + +name: Secrets scan + +on: + workflow_call: + + +jobs: + secrets-scan: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Run GitLeaks Scanner + uses: gitleaks/gitleaks-action@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.trivyignore b/.trivyignore new file mode 100644 index 0000000..b70fb68 --- /dev/null +++ b/.trivyignore @@ -0,0 +1,4 @@ +# Ignore known false-positive / accepted-risk CVEs for this project +# Add CVE IDs below to suppress them in Trivy image scan results + +# Example: CVE-2025-XXXX diff --git a/Dockerfile b/Dockerfile index 3b25e32..0a7f10d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,22 @@ -FROM eclipse-temurin:21-jdk-jammy +# Stage 1: Build +FROM eclipse-temurin:21-jdk-jammy AS build + WORKDIR /app -COPY . . -RUN chmod +x mvnw && ./mvnw clean package -DskipTests -B + +COPY pom.xml mvnw ./ +COPY .mvn .mvn +RUN chmod +x mvnw && ./mvnw dependency:go-offline -q + +COPY src ./src +RUN ./mvnw clean package -DskipTests -B + +# Stage 2: Run — slim final image +FROM eclipse-temurin:21-jre-jammy + +WORKDIR /app + +COPY --from=build /app/target/*.jar app.jar + EXPOSE 8080 -ENTRYPOINT ["sh", "-c", "java -jar target/*.jar"] + +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/docker-compose.yml b/docker-compose.yml index d19ae58..69af992 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,10 @@ services: image: mysql:8.0 container_name: bankapp-mysql environment: - MYSQL_ROOT_PASSWORD: Test@123 + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD} MYSQL_DATABASE: bankappdb + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} ports: - "3306:3306" volumes: @@ -29,7 +31,7 @@ services: - bankapp-net bankapp: - build: . + image: ${DOCKERHUB_USER}/bankapp:${DOCKER_TAG:-latest} container_name: bankapp ports: - "8080:8080" @@ -37,8 +39,8 @@ services: MYSQL_HOST: mysql MYSQL_PORT: 3306 MYSQL_DATABASE: bankappdb - MYSQL_USER: root - MYSQL_PASSWORD: Test@123 + MYSQL_USER: ${DB_USERNAME} + MYSQL_PASSWORD: ${DB_PASSWORD} OLLAMA_URL: http://ollama:11434 depends_on: mysql: diff --git a/pom.xml b/pom.xml index c9942e6..720a672 100644 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,30 @@ org.springframework.boot spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 + + google_checks.xml + true + true + + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.8.6.4 + + Max + High + true + + diff --git a/src/main/resources/static/css/bankapp.css b/src/main/resources/static/css/bankapp.css index ea47058..461a676 100644 --- a/src/main/resources/static/css/bankapp.css +++ b/src/main/resources/static/css/bankapp.css @@ -78,6 +78,24 @@ a:hover { gap: 0.5rem; } +.brand-shield { + color: var(--accent); + font-size: 1.1rem; +} + +.nav-user { + display: flex; + align-items: center; + gap: 0.4rem; + font-size: 0.875rem; + font-weight: 500; + color: var(--text-muted); + background: var(--glass-bg); + border: 1px solid var(--glass-border); + border-radius: 999px; + padding: 0.3rem 0.85rem; +} + .navbar-app .nav-link { color: var(--text-muted) !important; font-weight: 500; @@ -164,6 +182,19 @@ a:hover { color: var(--text-muted) !important; } +.auth-hero-icon { + width: 64px; + height: 64px; + background: linear-gradient(135deg, var(--accent), #818cf8); + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1.75rem; + color: #fff; + box-shadow: 0 4px 20px var(--accent-glow); +} + /* ===== Form Styles ===== */ .form-floating > .form-control { background: var(--bg-surface); @@ -260,6 +291,59 @@ a:hover { text-align: center; padding: 2.5rem 2rem; margin-bottom: 2rem; + border-top: 3px solid var(--accent); + position: relative; + overflow: hidden; +} + +.balance-card::before { + content: ''; + position: absolute; + top: 0; left: 0; right: 0; + height: 3px; + background: linear-gradient(90deg, var(--accent), #818cf8, var(--accent)); + background-size: 200% 100%; + animation: shimmer 3s linear infinite; +} + +@keyframes shimmer { + 0% { background-position: 200% 0; } + 100% { background-position: -200% 0; } +} + +.balance-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 1rem; + flex-wrap: wrap; +} + +.status-badge { + display: inline-flex; + align-items: center; + gap: 0.4rem; + font-size: 0.8rem; + font-weight: 600; + color: var(--success); + background: rgba(34, 197, 94, 0.12); + border: 1px solid rgba(34, 197, 94, 0.3); + border-radius: 999px; + padding: 0.25rem 0.75rem; +} + +.status-dot { + width: 7px; + height: 7px; + background: var(--success); + border-radius: 50%; + display: inline-block; + animation: pulse-dot 1.8s ease-in-out infinite; +} + +@keyframes pulse-dot { + 0%, 100% { opacity: 1; transform: scale(1); } + 50% { opacity: 0.5; transform: scale(0.7); } } .balance-label { @@ -450,6 +534,41 @@ a:hover { font-weight: 500; } +.footer-inner { + display: flex; + align-items: center; + justify-content: center; + gap: 1.5rem; + flex-wrap: wrap; +} + +.footer-badges { + display: flex; + gap: 0.5rem; +} + +.footer-badge { + display: inline-flex; + align-items: center; + gap: 0.3rem; + font-size: 0.75rem; + font-weight: 600; + border-radius: 999px; + padding: 0.2rem 0.65rem; +} + +.badge-security { + background: rgba(34, 197, 94, 0.12); + border: 1px solid rgba(34, 197, 94, 0.3); + color: var(--success); +} + +.badge-pipeline { + background: rgba(59, 130, 246, 0.12); + border: 1px solid rgba(59, 130, 246, 0.3); + color: var(--accent); +} + /* ===== Animations ===== */ @keyframes fadeInUp { from { diff --git a/src/main/resources/templates/dashboard.html b/src/main/resources/templates/dashboard.html index 9ee685e..11285dc 100644 --- a/src/main/resources/templates/dashboard.html +++ b/src/main/resources/templates/dashboard.html @@ -17,6 +17,12 @@ Account #1 Savings +
+ Active + + View Transactions + +
diff --git a/src/main/resources/templates/fragments/layout.html b/src/main/resources/templates/fragments/layout.html index 3cafa18..926b424 100644 --- a/src/main/resources/templates/fragments/layout.html +++ b/src/main/resources/templates/fragments/layout.html @@ -1,5 +1,6 @@ - + @@ -30,7 +31,7 @@