Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
9eef069
Test
VladyslavZakharov Feb 14, 2026
c65f635
task #4 done
VladyslavZakharov Feb 14, 2026
438464b
Add local run instructions and verification URLs (task #3)
SvetlanaAdamian Feb 14, 2026
21bc94c
feat: tasks #11, #13 - create pipeline with checkout and verify stages
Khanbibi-devops Feb 15, 2026
a85918e
Add local run guide from Vladyslav's branch
AyselMekhtieva Feb 16, 2026
17248ba
Add Trivy security scanning integration with Docker
AyselMekhtieva Feb 16, 2026
d956acc
Enforce pipeline failure on High/Critical vulnerabilities with Trivy
AyselMekhtieva Feb 16, 2026
b9faa7d
Update Trivy documentation: pipeline fails on High/Critical vulnerabi…
AyselMekhtieva Feb 16, 2026
f83fbe8
Add nginx configuration
saniyasabitova12 Feb 16, 2026
5df159a
Add static code checks for all services and CI integration
AyselMekhtieva Feb 16, 2026
3c64ae3
Archive test reports and build artifacts in CI
AyselMekhtieva Feb 16, 2026
88d051e
Add blue/green and nginx compose files
saniyasabitova12 Feb 16, 2026
480dad9
feat: task #13 - add build, test and package stages
Khanbibi-devops Feb 16, 2026
bb44f30
Merge pull request #1 from VladyslavZakharov/Vladyslav
VladyslavZakharov Feb 16, 2026
7e705f2
Fix green compose paths and ports for blue-green deployment
saniyasabitova12 Feb 16, 2026
02f45c8
Merge pull request #2 from VladyslavZakharov/Bibi
Khanbibi-devops Feb 16, 2026
4a29171
Restore documentation and CI workflow after merge
AyselMekhtieva Feb 16, 2026
4fa911e
task-6: add ports and dependencies analysis
SvetlanaAdamian Feb 16, 2026
616d9dc
Merge branch 'main' into Aysel
AyselMekhtieva Feb 16, 2026
8795db8
Merge pull request #3 from VladyslavZakharov/svetlana-task-6-identify…
VladyslavZakharov Feb 16, 2026
77f34dd
Merge pull request #5 from VladyslavZakharov/Sani
VladyslavZakharov Feb 16, 2026
eebcf72
Merge pull request #6 from VladyslavZakharov/Svetlana
VladyslavZakharov Feb 17, 2026
7d3c8ab
feat: add docker push stage
Khanbibi-devops Feb 17, 2026
e56a8c3
Fix debug port conflict for green env (use 9329 instead of 9229)
saniyasabitova12 Feb 17, 2026
f64fa12
Add deploy script for blue/green
saniyasabitova12 Feb 17, 2026
82b5375
feat: finalize task 13 with artifacts and versioning
Khanbibi-devops Feb 17, 2026
ede88e3
Add smart smoke tests after deployment
saniyasabitova12 Feb 17, 2026
6ab36ed
feat: upgrade CI to middle level
Khanbibi-devops Feb 17, 2026
f73b48c
Fix blue/green deploy ports and add smarter smoke tests
saniyasabitova12 Feb 17, 2026
d520f1e
Add manual rollback documentation
saniyasabitova12 Feb 17, 2026
0e25e57
Task #14: Add Docker image build stage with parallel builds
Khanbibi-devops Feb 17, 2026
c076cee
ttask-5: add baseline health verification steps
SvetlanaAdamian Feb 18, 2026
2b1e5de
Merge pull request #7 from VladyslavZakharov/Sani
VladyslavZakharov Feb 18, 2026
b464c08
Merge pull request #8 from VladyslavZakharov/Bibi
VladyslavZakharov Feb 18, 2026
096759e
Merge pull request #9 from VladyslavZakharov/Bibi-Task14
VladyslavZakharov Feb 18, 2026
4b643a9
Merge pull request #11 from VladyslavZakharov/svetlana-tasks-5-healt-…
VladyslavZakharov Feb 18, 2026
716061b
Update Jenkins pipeline configuration
AyselMekhtieva Feb 18, 2026
2f0a880
Update ci.yml
AyselMekhtieva Feb 18, 2026
09530fa
task #20 archive scan reports in Jenkins (updated Jenkinsfile).
AyselMekhtieva Feb 18, 2026
557dab4
Merge branch 'main' into Aysel
AyselMekhtieva Feb 25, 2026
1adc502
Merge pull request #4 from VladyslavZakharov/Aysel
AyselMekhtieva Feb 25, 2026
68c6fc2
Update app.py
AyselMekhtieva Feb 26, 2026
759a0db
Task 33: Add ENVIRONMENT parameter
Khanbibi-devops Feb 27, 2026
2a65c93
Merge pull request #17 from VladyslavZakharov/task-33-pipeline-parame…
VladyslavZakharov Feb 27, 2026
d1abe40
FIxing docker
VladyslavZakharov Feb 28, 2026
2477411
Update Docker image names in workflow files to use khanbibi repository
VladyslavZakharov Feb 28, 2026
f50b384
Fix formatting in app.py for route definition
VladyslavZakharov Feb 28, 2026
8292baa
Add ESLint configuration file for JavaScript linting
VladyslavZakharov Feb 28, 2026
e474540
Refactor code structure for improved readability and maintainability
VladyslavZakharov Feb 28, 2026
879ac42
Fix Redis reconnection logic formatting for improved clarity
VladyslavZakharov Feb 28, 2026
7a917b6
Fix formatting of Redis reconnection logic for consistency
VladyslavZakharov Feb 28, 2026
ea14c29
Merge pull request #23 from VladyslavZakharov/Vladfix
Khanbibi-devops Feb 28, 2026
9ad59a0
Add ESLint flat config for result service
AyselMekhtieva Mar 6, 2026
dab7d0a
Merge pull request #12 from VladyslavZakharov/Trying-to-fix-static-ch…
VladyslavZakharov Mar 6, 2026
27e1e4d
Add newline at end of file for consistency
VladyslavZakharov Mar 6, 2026
0dc6b51
fix
VladyslavZakharov Mar 20, 2026
0b6a29e
Add Devops audit report (Claude)
saniyasabitova12 Mar 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions .github/workflows/call-docker-build-result.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ jobs:
### tell docker where to push.
### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below
dockerhub-enable: true
ghcr-enable: true
ghcr-enable: false

### REQUIRED
### A list of the account/repo names for docker build. List should match what's enabled above
### defaults to:
image-names: |
ghcr.io/dockersamples/example-voting-app-result
dockersamples/examplevotingapp_result
khanbibi/example-voting-app-result

### REQUIRED set rules for tagging images, based on special action syntax:
### https://github.com/docker/metadata-action#tags-input
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/call-docker-build-vote.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ jobs:
### tell docker where to push.
### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below
dockerhub-enable: true
ghcr-enable: true
ghcr-enable: false

### REQUIRED
### A list of the account/repo names for docker build. List should match what's enabled above
### defaults to:
image-names: |
ghcr.io/dockersamples/example-voting-app-vote
dockersamples/examplevotingapp_vote
khanbibi/example-voting-app-vote

### REQUIRED set rules for tagging images, based on special action syntax:
### https://github.com/docker/metadata-action#tags-input
Expand Down
5 changes: 2 additions & 3 deletions .github/workflows/call-docker-build-worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ jobs:
### tell docker where to push.
### NOTE if Docker Hub is set to true, you must set secrets above and also add account/repo/tags below
dockerhub-enable: true
ghcr-enable: true
ghcr-enable: false

### REQUIRED
### A list of the account/repo names for docker build. List should match what's enabled above
### defaults to:
image-names: |
ghcr.io/dockersamples/example-voting-app-worker
dockersamples/examplevotingapp_worker
khanbibi/example-voting-app-worker

### REQUIRED set rules for tagging images, based on special action syntax:
### https://github.com/docker/metadata-action#tags-input
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
static-checks:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Run static checks for all services
run: |
chmod +x ./run-static-checks.sh
./run-static-checks.sh
3 changes: 3 additions & 0 deletions .trivyignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Trivy vulnerability ignore file
# Add CVE IDs to ignore specific vulnerabilities
# Format: CVE-YYYY-XXXXX
149 changes: 149 additions & 0 deletions AUDIT_REPORT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Repository Audit Report

**Date:** 2026-03-19
**Branch:** sani-review
**Scope:** Bugs, security issues, architecture problems, Docker/DevOps issues, CI/CD risks (Jenkins)

---

## CRITICAL

### C1 — Missing `path` import in `result/server.js` (Runtime Crash)
**File:** `result/server.js:71`
`res.sendFile(path.resolve(__dirname + "/views/index.html"))` — `path` is used but never imported with `require('path')`. Every GET `/` will throw a `ReferenceError`, making the result service completely broken in production.

### C2 — Hardcoded Database Credentials Everywhere
**Files:**
- `docker-compose.yml:67-68` — `POSTGRES_USER/PASSWORD: "postgres"`
- `worker/Program.cs:19` — `"Server=db;Username=postgres;Password=postgres;"`
- `result/server.js:21` — `"postgres://postgres:postgres@db/postgres"`
- `k8s-specifications/db-deployment.yaml:23-24` — plain-text in YAML
- `result/docker-compose.test.yml:49-51`

Credentials are in source control, visible to anyone. Should use environment variables + Kubernetes Secrets / Docker secrets.

### C3 — Docker Socket Mounted into Containers
**Files:** `trivy-scan.sh:15-37`, `docker-compose.trivy.yml:9`
`-v /var/run/docker.sock:/var/run/docker.sock` gives the container full control of the Docker daemon. A compromised container becomes a full host compromise.

### C4 — Jenkinsfile `|| true` Suppresses All Failures
**File:** `Jenkinsfile` (lines ~46, 64, 73, 82, 92)
Every critical step — linting, static analysis, security scanning — is suffixed with `|| true`. The pipeline will show green even when checks fail. Defeats the entire purpose of CI gates.

### C5 — PostgreSQL `emptyDir` in Kubernetes (Data Loss)
**Files:** `k8s-specifications/db-deployment.yaml:33`, `k8s-specifications/redis-deployment.yaml:28`
```yaml
volumes:
- name: db-data
emptyDir: {} # wiped on every pod restart
```
All vote data is permanently lost on any pod restart or eviction. Needs a `PersistentVolumeClaim`.

---

## MEDIUM

### M1 — Flask Debug Mode in Production
**File:** `vote/app.py:53`
```python
app.run(host='0.0.0.0', port=80, debug=True, threaded=True)
```
`debug=True` enables the Werkzeug interactive debugger — arbitrary code execution from the browser. Should use gunicorn (already in `requirements.txt`).

### M2 — No USER Directive in Any Dockerfile
**Files:** `vote/Dockerfile`, `result/Dockerfile`, `worker/Dockerfile`, `seed-data/Dockerfile`
All containers run as root. A container escape grants full host access. Add `USER nobody` or create a dedicated user.

### M3 — Unpinned Base Images
**Files:** All Dockerfiles use tags like `python:3.11-slim`, `node:18-slim`, `mcr.microsoft.com/dotnet/sdk:7.0` — no SHA digest pinning. Builds are non-reproducible and silently pick up upstream changes.

### M4 — `result/tests/Dockerfile` Uses Node 8 (EOL 2019)
**File:** `result/tests/Dockerfile:1` — `node:8.9-slim`. Critical CVEs, no patches. Upgrade to Node 18+.

### M5 — Test PostgreSQL Version Mismatch
**File:** `result/docker-compose.test.yml:48` uses `postgres:9.4` (EOL 2021).
**Production:** `docker-compose.yml` uses `postgres:15-alpine`. Tests don't reflect production behavior.

### M6 — Jenkinsfile Artifacts Reference Non-Existent Paths
**File:** `Jenkinsfile:134-135`
```groovy
'result/dist/**/*' // no dist/ directory in result service
'vote/build/**/*' // no build/ directory in vote service
```
Archive steps silently do nothing. No build output is preserved.

### M7 — JUnit Report Format Mismatch
**File:** `Jenkinsfile:137` — `junit testResults: 'result/tests/test-report.txt'`
`result/tests/tests.sh` writes plain text (`echo "Tests passed" >> test-report.txt`), not JUnit XML. Jenkins will fail or silently skip the report.

### M8 — Worker Has No Graceful Shutdown
**File:** `worker/Program.cs:29-61` — `while(true)` loop with no `CancellationToken` or SIGTERM handler. Rolling deployments will SIGKILL the worker mid-vote, potentially losing votes in transit.

### M9 — No Resource Limits Anywhere
**Files:** All `docker-compose.yml` services, all `k8s-specifications/*.yaml`
No memory or CPU limits. A single misbehaving container can starve the host.

### M10 — No Liveness/Readiness Probes in Kubernetes
**Files:** All `k8s-specifications/*-deployment.yaml`
Kubernetes cannot detect unhealthy pods; broken instances stay in rotation and receive traffic.

### M11 — Nginx Has No HTTPS, No Security Headers, No Rate Limiting
**File:** `nginx/nginx.conf`
- HTTP only (port 80), all traffic in plaintext
- Missing: `X-Frame-Options`, `X-Content-Type-Options`, `Content-Security-Policy`, HSTS
- No `limit_req` — open to abuse/DDoS

### M12 — Duplicate Build Stages in Jenkinsfile
**File:** `Jenkinsfile` — images are built once serially (lines ~30-38) then rebuilt again in a "parallel" stage (lines ~94-115). Wasted CI time and ambiguity about which artifact is tested.

### M13 — No `.dockerignore` Files
No `.dockerignore` found in any service directory. `.git`, `node_modules`, test files, and local configs are copied into images — larger images and potential secret leakage.

### M14 — Dependabot Only Covers GitHub Actions
**File:** `.github/dependabot.yml` — only `github-actions` ecosystem monitored. `npm`, `pip`, and `nuget` packages are unmonitored for CVEs.

---

## LOW

### L1 — Hardcoded Service Hostnames
`result/server.js:21` and `worker/Program.cs:19` hardcode the hostname `db`. These are not configurable via environment variables, making multi-environment deployments fragile.

### L2 — `nodemon` in Production Result Image
**File:** `result/Dockerfile:11` — `nodemon` installed globally in the production image. Development tool adds unnecessary attack surface.

### L3 — Git Short SHA as Image Tag
**File:** `Jenkinsfile:22-26` — 7-character short SHAs have collision risk in large repos and are not immutable identifiers for production traceability.

### L4 — Single Replica for All Kubernetes Deployments
All `k8s-specifications/*-deployment.yaml` set `replicas: 1`. Any pod failure causes downtime. Vote and Result services should be at ≥2.

### L5 — No Kubernetes Namespace
All K8s specs deploy to `default` namespace — no isolation, no RBAC scoping possible.

### L6 — `chmod +x` on Every Pipeline Run
**File:** `Jenkinsfile` — scripts have `chmod +x` applied each run rather than committing the executable bit with `git update-index --chmod=+x`.

### L7 — Redis and PostgreSQL Are Single Points of Failure
Both have no clustering, no replication, no backup strategy. Disk failure = data loss.

### L8 — No Observability Stack
No log aggregation, no metrics (Prometheus), no alerting, no distributed tracing. Failures will go undetected until users report them.

---

## Summary

| Severity | Count |
|----------|-------|
| Critical | 5 |
| Medium | 14 |
| Low | 8 |

**Top priority fixes in order:**
1. Add `const path = require('path')` to `result/server.js` — service is currently broken
2. Remove hardcoded DB credentials, inject via env vars
3. Replace `emptyDir` with `PersistentVolumeClaim` for Postgres in K8s
4. Remove `|| true` from all Jenkins pipeline steps
5. Remove Docker socket mount from Trivy container
6. Set `debug=False` in `vote/app.py` and switch to gunicorn
150 changes: 150 additions & 0 deletions Jenkinsfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
pipeline {
agent any

parameters {
choice(
name: 'ENVIRONMENT',
choices: ['dev', 'staging', 'prod'],
description: 'Target deployment environment'
)
}

environment {
IMAGE_TAG = ''
}

stages {

stage('Checkout') {
steps {
checkout scm
script {
IMAGE_TAG = sh(
script: "git rev-parse --short HEAD",
returnStdout: true
).trim()
}
}
}

stage('Build') {
steps {
echo '🔨 Building Docker images...'
sh '''
docker build -t voting-app-vote:latest ./vote
docker build -t voting-app-result:latest ./result
docker build -t voting-app-worker:latest ./worker
'''
}
}

stage('Static Code Checks') {
steps {
echo '🔍 Running static code analysis...'
sh '''
chmod +x ./run-static-checks.sh
./run-static-checks.sh || true
'''
}
}

stage('Security Scan') {
steps {
echo '🔒 Running Trivy vulnerability scan...'
sh '''
mkdir -p reports

docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/reports:/reports \
aquasec/trivy image \
--severity HIGH,CRITICAL \
--format json \
-o /reports/vote-scan-report.json \
voting-app-vote:latest || true

docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/reports:/reports \
aquasec/trivy image \
--severity HIGH,CRITICAL \
--format json \
-o /reports/result-scan-report.json \
voting-app-result:latest || true

docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v $(pwd)/reports:/reports \
aquasec/trivy image \
--severity HIGH,CRITICAL \
--format json \
-o /reports/worker-scan-report.json \
voting-app-worker:latest || true
'''
}
}

stage('Unit Tests') {
steps {
echo '🧪 Running tests...'
sh '''
chmod +x ./result/tests/tests.sh
./result/tests/tests.sh || true
'''
stage('Build (Parallel)') {
parallel {

stage('Vote') {
steps {
sh "docker build -t voting-app-vote:${IMAGE_TAG} ./vote"
}
}

stage('Result') {
steps {
sh "docker build -t voting-app-result:${IMAGE_TAG} ./result"
}
}

stage('Worker') {
steps {
sh "docker build -t voting-app-worker:${IMAGE_TAG} ./worker"
}
}
}
}

stage('Test') {
steps {
sh '''
echo "Running tests..." > test-report.txt
echo "All tests passed" >> test-report.txt
'''
}
}
}

post {
always {
echo '📊 Archiving scan reports and build artifacts...'

archiveArtifacts artifacts: 'reports/**/*.*', allowEmptyArchive: true, fingerprint: true
archiveArtifacts artifacts: 'result/tests/test-report.txt', allowEmptyArchive: true
archiveArtifacts artifacts: 'worker/bin/**/*.dll,worker/bin/**/*.exe', allowEmptyArchive: true
archiveArtifacts artifacts: 'result/dist/**/*', allowEmptyArchive: true
archiveArtifacts artifacts: 'vote/build/**/*', allowEmptyArchive: true

junit testResults: 'result/tests/test-report.txt', allowEmptyResults: true
}

success {
echo '✅ Build successful!'
}

failure {
echo '❌ Build failed!'
archiveArtifacts artifacts: 'test-report.txt', allowEmptyArchive: false
}
}
}

Loading