Skip to content

Commit f781b1e

Browse files
committed
Harden security flows and fix frontend lint blockers
1 parent e94af4c commit f781b1e

30 files changed

Lines changed: 711 additions & 419 deletions

.github/workflows/deploy-admin-backend-aws.yml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,15 +156,30 @@ jobs:
156156
print("✅ Admin route validation complete")
157157
EOF
158158
159+
secret-scan:
160+
name: Secret Scan (Gitleaks)
161+
runs-on: ubuntu-latest
162+
steps:
163+
- name: Checkout
164+
uses: actions/checkout@v4
165+
with:
166+
ref: ${{ env.DEPLOY_REF }}
167+
168+
- name: Run gitleaks
169+
uses: gitleaks/gitleaks-action@v2
170+
env:
171+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
172+
159173
quality-gate:
160174
name: Quality Gate
161-
needs: [lint, test, validate-sam, validate-routes]
175+
needs: [lint, test, validate-sam, validate-routes, secret-scan]
162176
runs-on: ubuntu-latest
163177
steps:
164178
- name: All checks passed
165179
run: |
166180
echo "## ✅ All Admin Backend Checks Passed" >> $GITHUB_STEP_SUMMARY
167181
echo "" >> $GITHUB_STEP_SUMMARY
182+
echo "- Secret scan: passed" >> $GITHUB_STEP_SUMMARY
168183
echo "Proceeding to deployment..." >> $GITHUB_STEP_SUMMARY
169184
170185
deploy:

.github/workflows/deploy-admin-frontend-aws.yml

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,28 @@ jobs:
6161
6262
- name: Install dependencies
6363
working-directory: admin-frontend
64-
run: npm install
64+
run: npm ci
65+
66+
- name: Run npm audit
67+
working-directory: admin-frontend
68+
run: |
69+
set +e
70+
npm audit --audit-level=high --json > audit-report.json
71+
STATUS=$?
72+
set -e
73+
if [ "$STATUS" -ne 0 ]; then
74+
echo "::error::Security vulnerabilities found in admin frontend dependencies"
75+
jq '.metadata.vulnerabilities' audit-report.json || true
76+
exit "$STATUS"
77+
fi
78+
79+
- name: Upload npm audit report
80+
uses: actions/upload-artifact@v4
81+
if: always()
82+
with:
83+
name: admin-frontend-audit-report
84+
path: admin-frontend/audit-report.json
85+
retention-days: 7
6586

6687
- name: Lint
6788
working-directory: admin-frontend
@@ -78,7 +99,7 @@ jobs:
7899
deploy:
79100
name: Deploy Admin Frontend
80101
runs-on: ubuntu-latest
81-
needs: lint-build
102+
needs: [lint-build, secret-scan]
82103
environment: ${{ github.ref_name == 'prod' && 'prod' || 'dev' }}
83104
env:
84105
S3_BUCKET: ${{ vars.ADMIN_FRONTEND_S3_BUCKET }}
@@ -116,7 +137,7 @@ jobs:
116137
117138
- name: Install dependencies
118139
working-directory: admin-frontend
119-
run: npm install
140+
run: npm ci
120141

121142
- name: Build
122143
working-directory: admin-frontend
@@ -130,3 +151,17 @@ jobs:
130151
- name: Invalidate CloudFront
131152
run: |
132153
aws cloudfront create-invalidation --distribution-id "$CLOUDFRONT_DISTRIBUTION_ID" --paths "/*"
154+
155+
secret-scan:
156+
name: Secret Scan (Gitleaks)
157+
runs-on: ubuntu-latest
158+
steps:
159+
- name: Checkout
160+
uses: actions/checkout@v4
161+
with:
162+
ref: ${{ env.DEPLOY_REF }}
163+
164+
- name: Run gitleaks
165+
uses: gitleaks/gitleaks-action@v2
166+
env:
167+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/deploy-backend-aws.yml

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,20 +97,71 @@ jobs:
9797
pip install pip-audit bandit
9898
9999
- name: Run pip-audit (dependency vulnerabilities)
100+
id: pip_audit
100101
working-directory: backend
101102
run: |
102103
# Audit declared requirements directly; avoid installing heavy runtime deps (e.g. manim)
103104
# that require OS libraries just to perform vulnerability checks.
105+
set +e
104106
if grep -qE "^[a-zA-Z]" requirements.txt 2>/dev/null; then
105-
pip-audit -r requirements.txt || echo "::warning::Security vulnerabilities found in dependencies"
107+
pip-audit -r requirements.txt -f json -o pip-audit-report.json
108+
STATUS=$?
106109
else
107110
echo "No external dependencies to audit"
111+
printf '{"message":"No external dependencies to audit"}\n' > pip-audit-report.json
112+
STATUS=0
113+
fi
114+
set -e
115+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
116+
if [ "$STATUS" -ne 0 ]; then
117+
echo "::error::pip-audit detected vulnerabilities"
108118
fi
109119
110120
- name: Run Bandit (security linter)
121+
id: bandit_scan
111122
working-directory: backend
112123
run: |
113-
bandit -r . -x ./tests -ll -ii --format github || echo "::warning::Potential security issues found"
124+
set +e
125+
bandit -r . -x ./tests -ll -ii -f json -o bandit-report.json
126+
STATUS=$?
127+
set -e
128+
echo "status=$STATUS" >> "$GITHUB_OUTPUT"
129+
if [ "$STATUS" -ne 0 ]; then
130+
echo "::error::Bandit detected security findings"
131+
fi
132+
133+
- name: Upload backend security reports
134+
uses: actions/upload-artifact@v4
135+
if: always()
136+
with:
137+
name: backend-security-reports
138+
path: |
139+
backend/pip-audit-report.json
140+
backend/bandit-report.json
141+
retention-days: 7
142+
143+
- name: Enforce security gate
144+
run: |
145+
if [ "${{ steps.pip_audit.outputs.status }}" != "0" ]; then
146+
exit 1
147+
fi
148+
if [ "${{ steps.bandit_scan.outputs.status }}" != "0" ]; then
149+
exit 1
150+
fi
151+
152+
secret-scan:
153+
name: Secret Scan (Gitleaks)
154+
runs-on: ubuntu-latest
155+
steps:
156+
- name: Checkout
157+
uses: actions/checkout@v4
158+
with:
159+
ref: ${{ env.DEPLOY_REF }}
160+
161+
- name: Run gitleaks
162+
uses: gitleaks/gitleaks-action@v2
163+
env:
164+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
114165

115166
test:
116167
name: Unit Tests
@@ -346,7 +397,7 @@ jobs:
346397

347398
quality-gate:
348399
name: Quality Gate
349-
needs: [lint, security, test, validate-sam, validate-routes]
400+
needs: [lint, security, secret-scan, test, validate-sam, validate-routes]
350401
runs-on: ubuntu-latest
351402
steps:
352403
- name: All checks passed
@@ -357,6 +408,7 @@ jobs:
357408
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
358409
echo "| Lint & Code Quality | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
359410
echo "| Security Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
411+
echo "| Secret Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
360412
echo "| Unit Tests | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
361413
echo "| SAM Validation | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
362414
echo "| Route Validation | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
@@ -375,6 +427,10 @@ jobs:
375427
FIREBASE_PROJECT_ID: ${{ vars.BACKEND_FIREBASE_PROJECT_ID || 'fpai-cbcb2' }}
376428
OPENAI_MODEL: ${{ vars.BACKEND_OPENAI_MODEL || 'gpt-5.2' }}
377429
API_KEYS_SECRET_ID: ${{ vars.BACKEND_API_KEYS_SECRET_ID }}
430+
ALLOWED_ORIGINS: ${{ vars.BACKEND_ALLOWED_ORIGINS || 'http://localhost:3000,http://localhost:5173,https://fpai.io,https://d1gyb6wodlq2q2.cloudfront.net' }}
431+
ASSETS_ALLOWED_ORIGINS: ${{ vars.BACKEND_ASSETS_ALLOWED_ORIGINS || 'https://fpai.io,http://localhost:3000' }}
432+
SHARE_PUBLIC_BASE_URL: ${{ vars.BACKEND_SHARE_PUBLIC_BASE_URL || 'https://fpai.io' }}
433+
REQUIRE_BRAVE_API_KEY: ${{ vars.BACKEND_REQUIRE_BRAVE_API_KEY || 'true' }}
378434
steps:
379435
- name: Checkout
380436
uses: actions/checkout@v4
@@ -418,7 +474,9 @@ jobs:
418474
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
419475
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
420476
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
477+
BRAVE_API_KEY: ${{ secrets.BRAVE_API_KEY }}
421478
FIREBASE_SERVICE_ACCOUNT_JSON: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_JSON }}
479+
REQUIRE_BRAVE_API_KEY: ${{ env.REQUIRE_BRAVE_API_KEY }}
422480
run: |
423481
if [ -z "$OPENAI_API_KEY" ]; then
424482
echo "::error::Missing GitHub secret OPENAI_API_KEY"
@@ -433,6 +491,10 @@ jobs:
433491
echo "::error::Missing GitHub secret FIREBASE_SERVICE_ACCOUNT_JSON"
434492
exit 1
435493
fi
494+
if [ "${REQUIRE_BRAVE_API_KEY}" != "false" ] && [ -z "$BRAVE_API_KEY" ]; then
495+
echo "::error::Missing GitHub secret BRAVE_API_KEY"
496+
exit 1
497+
fi
436498
437499
if [ "${{ env.ENVIRONMENT }}" = "prod" ]; then
438500
DEFAULT_SECRET_ID="fpai/api-keys"
@@ -457,8 +519,9 @@ jobs:
457519
UPDATED_SECRET=$(printf '%s' "$CURRENT_SECRET" \
458520
| jq --arg openai_key "$OPENAI_API_KEY" \
459521
--arg gemini_key "$EFFECTIVE_GEMINI_API_KEY" \
522+
--arg brave_key "$BRAVE_API_KEY" \
460523
--arg firebase_sa "$FIREBASE_SERVICE_ACCOUNT_JSON" \
461-
'.OPENAI_API_KEY = $openai_key | .GEMINI_API_KEY = $gemini_key | .FIREBASE_SERVICE_ACCOUNT_JSON = $firebase_sa')
524+
'.OPENAI_API_KEY = $openai_key | .GEMINI_API_KEY = $gemini_key | .BRAVE_API_KEY = $brave_key | .FIREBASE_SERVICE_ACCOUNT_JSON = $firebase_sa')
462525
463526
if [ "$SECRET_EXISTS" = "true" ]; then
464527
aws secretsmanager put-secret-value \
@@ -470,7 +533,32 @@ jobs:
470533
--secret-string "$UPDATED_SECRET"
471534
fi
472535
473-
echo "✅ Updated OPENAI_API_KEY + GEMINI_API_KEY + FIREBASE_SERVICE_ACCOUNT_JSON in Secrets Manager ($SECRET_ID)"
536+
echo "✅ Updated OPENAI_API_KEY + GEMINI_API_KEY + BRAVE_API_KEY + FIREBASE_SERVICE_ACCOUNT_JSON in Secrets Manager ($SECRET_ID)"
537+
538+
- name: Validate required Secrets Manager fields
539+
env:
540+
REQUIRE_BRAVE_API_KEY: ${{ env.REQUIRE_BRAVE_API_KEY }}
541+
run: |
542+
if [ "${{ env.ENVIRONMENT }}" = "prod" ]; then
543+
DEFAULT_SECRET_ID="fpai/api-keys"
544+
else
545+
DEFAULT_SECRET_ID="fpai/api-keys-${{ env.ENVIRONMENT }}"
546+
fi
547+
SECRET_ID="${API_KEYS_SECRET_ID:-$DEFAULT_SECRET_ID}"
548+
SECRET_JSON=$(aws secretsmanager get-secret-value \
549+
--secret-id "$SECRET_ID" \
550+
--query SecretString \
551+
--output text)
552+
553+
echo "$SECRET_JSON" | jq -e '
554+
((.OPENAI_API_KEY // "") | length > 0) and
555+
((.GEMINI_API_KEY // "") | length > 0) and
556+
((.FIREBASE_SERVICE_ACCOUNT_JSON // "") | length > 0)
557+
' >/dev/null
558+
559+
if [ "${REQUIRE_BRAVE_API_KEY}" != "false" ]; then
560+
echo "$SECRET_JSON" | jq -e '(.BRAVE_API_KEY // "") | length > 0' >/dev/null
561+
fi
474562
475563
- name: SAM Build
476564
working-directory: backend
@@ -486,6 +574,9 @@ jobs:
486574
fi
487575
488576
SECRET_ID="${API_KEYS_SECRET_ID:-$DEFAULT_SECRET_ID}"
577+
ALLOWED_ORIGINS_CLEAN=$(echo "${ALLOWED_ORIGINS}" | tr -d '[:space:]')
578+
ASSETS_ALLOWED_ORIGINS_CLEAN=$(echo "${ASSETS_ALLOWED_ORIGINS}" | tr -d '[:space:]')
579+
SHARE_PUBLIC_BASE_URL_CLEAN=$(echo "${SHARE_PUBLIC_BASE_URL}" | tr -d '[:space:]')
489580
490581
if [ "${{ env.ENVIRONMENT }}" = "prod" ]; then
491582
BETA_GATE_ENABLED=true
@@ -500,7 +591,7 @@ jobs:
500591
--resolve-s3 \
501592
--resolve-image-repos \
502593
--capabilities CAPABILITY_IAM \
503-
--parameter-overrides "Environment=${{ env.ENVIRONMENT }} OpenAIModel=${OPENAI_MODEL} FirebaseProjectId=${FIREBASE_PROJECT_ID} BetaGateEnabled=${BETA_GATE_ENABLED} EnableJobEventStreaming=true ApiKeysSecretId=${SECRET_ID}"
594+
--parameter-overrides "Environment=${{ env.ENVIRONMENT }} OpenAIModel=${OPENAI_MODEL} FirebaseProjectId=${FIREBASE_PROJECT_ID} BetaGateEnabled=${BETA_GATE_ENABLED} EnableJobEventStreaming=true ApiKeysSecretId=${SECRET_ID} AllowedOrigins=${ALLOWED_ORIGINS_CLEAN} AssetsAllowedOrigins=${ASSETS_ALLOWED_ORIGINS_CLEAN} SharePublicBaseUrl=${SHARE_PUBLIC_BASE_URL_CLEAN}"
504595
505596
- name: Get API Endpoint
506597
id: get-endpoint

.github/workflows/deploy-frontend-aws.yml

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,43 @@ jobs:
8585
- name: Run npm audit
8686
working-directory: frontend
8787
run: |
88-
npm audit --audit-level=high || {
89-
echo "::warning::Security vulnerabilities found in dependencies"
90-
npm audit --json > audit-report.json || true
91-
}
88+
set +e
89+
npm audit --audit-level=high --json > audit-report.json
90+
STATUS=$?
91+
set -e
92+
if [ "$STATUS" -ne 0 ]; then
93+
echo "::error::Security vulnerabilities found in dependencies"
94+
jq '.metadata.vulnerabilities' audit-report.json || true
95+
exit "$STATUS"
96+
fi
97+
98+
- name: Upload npm audit report
99+
uses: actions/upload-artifact@v4
100+
if: always()
101+
with:
102+
name: frontend-audit-report
103+
path: frontend/audit-report.json
104+
retention-days: 7
92105

93106
- name: Check for outdated dependencies
94107
working-directory: frontend
95108
run: |
96109
npm outdated || echo "Some packages are outdated"
97110
111+
secret-scan:
112+
name: Secret Scan (Gitleaks)
113+
runs-on: ubuntu-latest
114+
steps:
115+
- name: Checkout
116+
uses: actions/checkout@v4
117+
with:
118+
ref: ${{ env.DEPLOY_REF }}
119+
120+
- name: Run gitleaks
121+
uses: gitleaks/gitleaks-action@v2
122+
env:
123+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
124+
98125
build-test:
99126
name: Build Verification
100127
runs-on: ubuntu-latest
@@ -265,7 +292,7 @@ jobs:
265292

266293
quality-gate:
267294
name: Quality Gate
268-
needs: [lint, security, build-test]
295+
needs: [lint, security, secret-scan, build-test]
269296
runs-on: ubuntu-latest
270297
steps:
271298
- name: All checks passed
@@ -276,6 +303,7 @@ jobs:
276303
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
277304
echo "| Lint & Code Quality | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
278305
echo "| Security Audit | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
306+
echo "| Secret Scan | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
279307
echo "| Build Verification | ✅ Passed |" >> $GITHUB_STEP_SUMMARY
280308
echo "" >> $GITHUB_STEP_SUMMARY
281309
echo "Proceeding to deployment..." >> $GITHUB_STEP_SUMMARY

0 commit comments

Comments
 (0)