From 76fe434e66bd9c0e2a56ef4f8580331b55ad511b Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Thu, 8 May 2025 23:05:15 +0100 Subject: [PATCH 1/7] Create docker-image.yml --- .github/workflows/docker-image.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..2579e17 --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,18 @@ +name: Docker Image CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag opsgo:$(date +%s) From 8582b954ad689ef50f765a62bf0c8ef16364357a Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Fri, 9 May 2025 00:00:21 +0100 Subject: [PATCH 2/7] full ai featured scan --- .github/scripts/comment_on_pr.py | 29 ++++++++++++++++++ .github/scripts/send_to_gemini.py | 49 ++++++++++++++++++++++++++++++ .github/workflows/ai-sec-scam.yaml | 46 ++++++++++++++++++++++++++++ .github/workflows/docker-image.yml | 18 ----------- 4 files changed, 124 insertions(+), 18 deletions(-) create mode 100644 .github/scripts/comment_on_pr.py create mode 100644 .github/scripts/send_to_gemini.py create mode 100644 .github/workflows/ai-sec-scam.yaml delete mode 100644 .github/workflows/docker-image.yml diff --git a/.github/scripts/comment_on_pr.py b/.github/scripts/comment_on_pr.py new file mode 100644 index 0000000..c19c8ea --- /dev/null +++ b/.github/scripts/comment_on_pr.py @@ -0,0 +1,29 @@ +import os +import requests + +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +REPO = os.getenv("GITHUB_REPOSITORY") # e.g., bashizip/opsGo +PR_NUMBER = os.getenv("GITHUB_PR_NUMBER") +COMMENT_FILE = "gemini-response.txt" + +# Load AI-generated suggestions +with open(COMMENT_FILE, "r") as f: + comment_body = f.read() + +# Prepare GitHub API request +url = f"https://api.github.com/repos/{REPO}/issues/{PR_NUMBER}/comments" +headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "Accept": "application/vnd.github.v3+json" +} + +payload = {"body": f"\ud83e\udde0 **AI Suggestions from Gemini**\n\n{comment_body}"} + +# Post comment to PR +response = requests.post(url, headers=headers, json=payload) + +if response.status_code == 201: + print("Successfully posted AI suggestions to the PR.") +else: + print("Failed to post comment:", response.status_code, response.text) + exit(1) diff --git a/.github/scripts/send_to_gemini.py b/.github/scripts/send_to_gemini.py new file mode 100644 index 0000000..fee275b --- /dev/null +++ b/.github/scripts/send_to_gemini.py @@ -0,0 +1,49 @@ +import os +import json +import requests + +TRIVY_FILE = "trivy-results.json" +OUTPUT_FILE = "gemini-response.txt" +API_KEY = os.getenv("GEMINI_API_KEY") +GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent" + +# Load Trivy scan results +with open(TRIVY_FILE, "r") as f: + trivy_data = json.load(f) + +# Prepare prompt for Gemini +prompt_text = f""" +The following is a vulnerability scan of a Docker image using Trivy. +Please analyze the issues and suggest secure fixes for each finding. + +```json +{json.dumps(trivy_data, indent=2)} +``` +""" + +# Create request payload +payload = { + "contents": [{ + "parts": [{"text": prompt_text}] + }] +} + +# Send request to Gemini +response = requests.post( + GEMINI_ENDPOINT, + headers={ + "Content-Type": "application/json", + "Authorization": f"Bearer {API_KEY}" + }, + json=payload +) + +# Save response +if response.status_code == 200: + ai_reply = response.json()["candidates"][0]["content"]["parts"][0]["text"] + with open(OUTPUT_FILE, "w") as out: + out.write(ai_reply) + print("AI suggestions saved to gemini-response.txt") +else: + print("Error from Gemini:", response.text) + exit(1) diff --git a/.github/workflows/ai-sec-scam.yaml b/.github/workflows/ai-sec-scam.yaml new file mode 100644 index 0000000..a387e50 --- /dev/null +++ b/.github/workflows/ai-sec-scam.yaml @@ -0,0 +1,46 @@ +name: AI-Powered Docker Security Scan + +on: + pull_request: + branches: [ "main" ] + types: [opened, synchronize] + +jobs: + ai-sec-docker-scan: + name: Scan Docker Image and Generate AI Suggestions + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Python dependencies + run: pip install requests + + - name: Build Docker image + run: docker build -t opsgo-ai-scan:latest -f Dockerfile . + + - name: Run Trivy scan on Docker image + uses: aquasecurity/trivy-action@0.13.0 + with: + scan-type: 'image' + image-ref: 'opsgo-ai-scan:latest' + format: 'json' + output: 'trivy-results.json' + + - name: Analyze results with Gemini + run: python .github/scripts/send_to_gemini.py + env: + GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }} + + - name: Post AI suggestions to PR + run: python .github/scripts/comment_on_pr.py + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }} diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml deleted file mode 100644 index 2579e17..0000000 --- a/.github/workflows/docker-image.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Docker Image CI - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Build the Docker image - run: docker build . --file Dockerfile --tag opsgo:$(date +%s) From 7ee674b7b9aac5932bd8444c0d47e014dafde5e6 Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Fri, 9 May 2025 00:26:29 +0100 Subject: [PATCH 3/7] use gemini studio api not vertex --- .github/scripts/send_to_gemini.py | 47 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/.github/scripts/send_to_gemini.py b/.github/scripts/send_to_gemini.py index fee275b..51c2c77 100644 --- a/.github/scripts/send_to_gemini.py +++ b/.github/scripts/send_to_gemini.py @@ -5,13 +5,19 @@ TRIVY_FILE = "trivy-results.json" OUTPUT_FILE = "gemini-response.txt" API_KEY = os.getenv("GEMINI_API_KEY") -GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent" +GEMINI_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent" + +if not API_KEY: + raise EnvironmentError("❌ GEMINI_API_KEY environment variable is not set.") + +# Append API key as URL query parameter +url = f"{GEMINI_ENDPOINT}?key={API_KEY}" # Load Trivy scan results with open(TRIVY_FILE, "r") as f: trivy_data = json.load(f) -# Prepare prompt for Gemini +# Build the prompt prompt_text = f""" The following is a vulnerability scan of a Docker image using Trivy. Please analyze the issues and suggest secure fixes for each finding. @@ -21,29 +27,34 @@ ``` """ -# Create request payload +# Prepare the request payload payload = { - "contents": [{ - "parts": [{"text": prompt_text}] - }] + "contents": [ + { + "parts": [{"text": prompt_text}] + } + ] } -# Send request to Gemini +# Make the POST request response = requests.post( - GEMINI_ENDPOINT, - headers={ - "Content-Type": "application/json", - "Authorization": f"Bearer {API_KEY}" - }, + url, + headers={"Content-Type": "application/json"}, json=payload ) -# Save response +# Handle the response if response.status_code == 200: - ai_reply = response.json()["candidates"][0]["content"]["parts"][0]["text"] - with open(OUTPUT_FILE, "w") as out: - out.write(ai_reply) - print("AI suggestions saved to gemini-response.txt") + try: + ai_reply = response.json()["candidates"][0]["content"]["parts"][0]["text"] + with open(OUTPUT_FILE, "w") as out: + out.write(ai_reply) + print("✅ AI suggestions saved to gemini-response.txt") + except (KeyError, IndexError): + print("⚠️ Received unexpected response format:") + print(response.json()) + exit(1) else: - print("Error from Gemini:", response.text) + print("❌ Error from Gemini:", response.status_code) + print(response.text) exit(1) From 5b15c0c9aa3fd7ce8ab2d168faa449ce28f9a936 Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Sat, 10 May 2025 10:20:08 +0100 Subject: [PATCH 4/7] bumped golang image version --- Dockerfile | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8e9c066..4f0a560 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # # Modified by Patrick Bashizi in March 2025 for educational purposes -FROM golang:1.19.2 as builder +FROM golang:1.24.3 as builder WORKDIR /app RUN go mod init opsgo COPY *.go ./ diff --git a/main.go b/main.go index 3bc6b17..c9a5772 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( "net/http" ) -const port string = ":8080" +const port string = ":8081" func main() { http.HandleFunc("/blue", blueHandler) From 2f2422bb985fc2c7ec595b5e0653308145e1fa7b Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Sat, 10 May 2025 13:15:22 +0100 Subject: [PATCH 5/7] some modifications --- Dockerfile | 2 +- main.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4f0a560..8e9c066 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # # Modified by Patrick Bashizi in March 2025 for educational purposes -FROM golang:1.24.3 as builder +FROM golang:1.19.2 as builder WORKDIR /app RUN go mod init opsgo COPY *.go ./ diff --git a/main.go b/main.go index c9a5772..3bc6b17 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( "net/http" ) -const port string = ":8081" +const port string = ":8080" func main() { http.HandleFunc("/blue", blueHandler) From 10b3d120b833cc74cb5c25dab8ba25dfffe2001c Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Sat, 10 May 2025 13:22:16 +0100 Subject: [PATCH 6/7] changed default port to 8282 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index 3bc6b17..ba49098 100644 --- a/main.go +++ b/main.go @@ -25,7 +25,7 @@ import ( "net/http" ) -const port string = ":8080" +const port string = ":8282" func main() { http.HandleFunc("/blue", blueHandler) From bb33d4fa85fcbbb53645475f4d077cbb5171deab Mon Sep 17 00:00:00 2001 From: Patrick Bashizi Date: Sat, 10 May 2025 13:33:06 +0100 Subject: [PATCH 7/7] fixes vulnerabilities in the dockerfile --- Dockerfile | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8e9c066..c7f808b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,15 +14,26 @@ # # Modified by Patrick Bashizi in March 2025 for educational purposes -FROM golang:1.19.2 as builder -WORKDIR /app -RUN go mod init opsgo -COPY *.go ./ -RUN CGO_ENABLED=0 GOOS=linux go build -o /opsgo +FROM debian:11.10 -FROM gcr.io/distroless/base-debian11 -WORKDIR / -COPY --from=builder /opsgo /opsgo -ENV PORT 8080 +# Update package lists and install necessary dependencies +RUN apt-get update && apt-get install -y --no-install-recommends \ + libc6=2.31-13+deb11u12 \ + libssl1.1=1.1.1w-0+deb11u2 \ + openssl=1.1.1w-0+deb11u2 \ + tzdata=2025b-0+deb11u1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Copy application files +COPY /opsgo /opsgo + +# Set environment variables +ENV PORT=8080 + +# Create a non-root user +RUN groupadd -r nonroot && useradd -r -g nonroot nonroot USER nonroot:nonroot + +# Command to run the application CMD ["/opsgo"] \ No newline at end of file