chore: Enhance CI workflow summary and improve JWT token generation
All checks were successful
CI / skip-ci-check (pull_request) Successful in 1m29s
CI / lint-and-type-check (pull_request) Successful in 2m6s
CI / python-lint (pull_request) Successful in 1m47s
CI / test-backend (pull_request) Successful in 3m8s
CI / build (pull_request) Successful in 2m26s
CI / secret-scanning (pull_request) Successful in 1m43s
CI / dependency-scan (pull_request) Successful in 1m35s
CI / sast-scan (pull_request) Successful in 2m46s
CI / workflow-summary (pull_request) Successful in 1m27s

This commit updates the CI workflow summary to provide a clearer overview of job results and their purposes. It also modifies the JWT token generation in the authentication API to include a unique identifier (`jti`) for both access and refresh tokens, improving token management. Additionally, the test for the token refresh endpoint is adjusted to ensure it verifies the new access token correctly.
This commit is contained in:
Tanya 2026-01-08 14:15:08 -05:00
parent 70cd7aad95
commit 922c468e9b
3 changed files with 63 additions and 28 deletions

View File

@ -506,22 +506,39 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY || true
if [ "$LEAK_COUNT" -gt 0 ]; then
echo "### Leak Details" >> $GITHUB_STEP_SUMMARY || true
echo "### Leak Summary Table" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "| File | Line | Rule | Description | Commit |" >> $GITHUB_STEP_SUMMARY || true
echo "|------|------|------|-------------|--------|" >> $GITHUB_STEP_SUMMARY || true
echo "| File | Line | Rule | Entropy | Commit | Author | Date |" >> $GITHUB_STEP_SUMMARY || true
echo "|------|------|------|---------|--------|--------|------|" >> $GITHUB_STEP_SUMMARY || true
# Extract and display leak details
jq -r '.[] | "| \(.File) | \(.Line) | \(.RuleID) | \(.Description // "N/A") | \(.Commit // "N/A") |"' gitleaks-report.json >> $GITHUB_STEP_SUMMARY || true
# Extract and display leak details in table format
jq -r '.[] | "| \(.File) | \(.Line) | \(.RuleID) | \(.Entropy // "N/A") | `\(.Commit[0:8])` | \(.Author // "N/A") | \(.Date // "N/A") |"' gitleaks-report.json >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "### Detailed Findings" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
# Display each finding with full details
jq -r '.[] |
"#### Finding: \(.RuleID) in \(.File):\(.Line)\n" +
"- **File:** `\(.File)`\n" +
"- **Line:** \(.Line)\n" +
"- **Rule ID:** \(.RuleID)\n" +
"- **Description:** \(.Description // "N/A")\n" +
"- **Entropy:** \(.Entropy // "N/A")\n" +
"- **Commit:** `\(.Commit)`\n" +
"- **Author:** \(.Author // "N/A") (\(.Email // "N/A"))\n" +
"- **Date:** \(.Date // "N/A")\n" +
"- **Fingerprint:** `\(.Fingerprint // "N/A")`\n"' gitleaks-report.json >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "### Full Report (JSON)" >> $GITHUB_STEP_SUMMARY || true
echo '```json' >> $GITHUB_STEP_SUMMARY || true
cat gitleaks-report.json >> $GITHUB_STEP_SUMMARY || true
echo '```' >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "⚠️ **Action Required:** Review and remove the secrets found above." >> $GITHUB_STEP_SUMMARY || true
echo "⚠️ **Action Required:** Review and remove the secrets found above. Secrets should be removed from the codebase and rotated if they were ever exposed." >> $GITHUB_STEP_SUMMARY || true
else
echo "✅ No secrets detected!" >> $GITHUB_STEP_SUMMARY || true
fi
@ -595,22 +612,36 @@ jobs:
steps:
- name: Generate workflow summary
run: |
echo "## 🔍 CI Workflow Summary" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "### Job Results" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "| Job | Status |" >> $GITHUB_STEP_SUMMARY || true
echo "|-----|--------|" >> $GITHUB_STEP_SUMMARY || true
echo "| 📝 Lint & Type Check | ${{ needs.lint-and-type-check.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🐍 Python Lint | ${{ needs.python-lint.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🧪 Backend Tests | ${{ needs.test-backend.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🏗️ Build | ${{ needs.build.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🔐 Secret Scanning | ${{ needs.secret-scanning.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 📦 Dependency Scan | ${{ needs.dependency-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "| 🔍 SAST Scan | ${{ needs.sast-scan.result }} |" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "### 📊 Summary" >> $GITHUB_STEP_SUMMARY || true
echo "" >> $GITHUB_STEP_SUMMARY || true
echo "All security and validation checks have completed." >> $GITHUB_STEP_SUMMARY || true
{
echo "## 🔍 CI Workflow Summary"
echo ""
echo "This table gives a **plain-English overview** of what ran in this pipeline and whether it passed."
echo ""
echo "### Job Results"
echo ""
echo "| Job | What it does | Status |"
echo "|-----|--------------|--------|"
echo "| 📝 Lint & Type Check | Runs ESLint on the admin UI and TypeScript type-checks the viewer UI | ${{ needs.lint-and-type-check.result }} |"
echo "| 🐍 Python Lint | Runs Python style and syntax checks over the backend | ${{ needs.python-lint.result }} |"
echo "| 🧪 Backend Tests | Runs \`pytest tests/ -v\` against the FastAPI backend (with coverage) | ${{ needs.test-backend.result }} |"
echo "| 🏗️ Build | Builds the admin frontend (Vite) and viewer frontend (Next.js) | ${{ needs.build.result }} |"
echo "| 🔐 Secret Scanning | Uses Gitleaks to look for committed secrets | ${{ needs.secret-scanning.result }} |"
echo "| 📦 Dependency Scan | Uses Trivy to scan dependencies for HIGH/CRITICAL vulns | ${{ needs.dependency-scan.result }} |"
echo "| 🔍 SAST Scan | Uses Semgrep to look for insecure code patterns | ${{ needs.sast-scan.result }} |"
echo ""
echo "Legend for the **Status** column:"
echo "- \`success\`: job finished and all checks/tests passed."
echo "- \`failure\`: job ran but one or more checks/tests failed (see that job's log)."
echo "- \`cancelled\`: job was stopped before finishing."
echo "- \`skipped\`: job did not run, usually because CI was skipped for this commit."
echo ""
echo "### 📊 How to read the backend test results"
echo ""
echo "- The **Backend Tests** row tells you if the test run as a whole passed or failed."
echo "- To see which specific tests failed or how they ran:"
echo " 1. Open the **test-backend** job in this workflow run."
echo " 2. Look at the **Run backend tests** step to see the \`pytest -v\` output."
echo " 3. For local debugging, run \`pytest tests/ -v\` in your dev environment."
} >> \"${GITHUB_STEP_SUMMARY:-/dev/stdout}\"
continue-on-error: true

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import os
import uuid
from datetime import datetime, timedelta
from typing import Annotated
@ -87,7 +88,7 @@ def create_access_token(data: dict, expires_delta: timedelta) -> str:
"""Create JWT access token."""
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
to_encode.update({"exp": expire, "jti": str(uuid.uuid4())})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
@ -95,7 +96,7 @@ def create_refresh_token(data: dict) -> str:
"""Create JWT refresh token."""
to_encode = data.copy()
expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
to_encode.update({"exp": expire, "type": "refresh"})
to_encode.update({"exp": expire, "type": "refresh", "jti": str(uuid.uuid4())})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)

View File

@ -126,7 +126,7 @@ class TestTokenRefresh:
"""Test token refresh endpoint."""
def test_refresh_token_success(
self, test_client: TestClient, auth_token: str
self, test_client: TestClient
):
"""Verify successful token refresh."""
# Get refresh token from login
@ -134,7 +134,10 @@ class TestTokenRefresh:
"/api/v1/auth/login",
json={"username": "testadmin", "password": "testpass"}
)
refresh_token = login_response.json()["refresh_token"]
assert login_response.status_code == 200
login_data = login_response.json()
initial_access_token = login_data["access_token"]
refresh_token = login_data["refresh_token"]
# Use refresh token to get new access token
response = test_client.post(
@ -146,7 +149,7 @@ class TestTokenRefresh:
data = response.json()
assert "access_token" in data
assert "refresh_token" in data
assert data["access_token"] != auth_token # Should be different token
assert data["access_token"] != initial_access_token # Should be different token
def test_refresh_token_with_invalid_token(self, test_client: TestClient):
"""Verify 401 with invalid refresh token."""