-
Notifications
You must be signed in to change notification settings - Fork 0
fix: docker healthcheck, CI optimization, and container hardening #436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
efed8e3
9120ee5
5b3202f
afc8e69
c2eb02a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -110,24 +110,59 @@ jobs: | |||||||||||||||||||||||
| id: scan-ref | ||||||||||||||||||||||||
| run: echo "ref=ghcr.io/aureliolo/synthorg-backend:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Trivy scan (critical — hard fail) | ||||||||||||||||||||||||
| # Single Trivy run for CRITICAL + HIGH (saves ~30s vs two separate runs). | ||||||||||||||||||||||||
| # JSON output lets us fail on CRITICAL only while still reporting HIGH. | ||||||||||||||||||||||||
| - name: Trivy scan | ||||||||||||||||||||||||
| uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 | ||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||
| image-ref: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| format: table | ||||||||||||||||||||||||
| exit-code: "1" | ||||||||||||||||||||||||
| severity: CRITICAL | ||||||||||||||||||||||||
| format: json | ||||||||||||||||||||||||
| output: trivy-backend.json | ||||||||||||||||||||||||
| exit-code: "0" | ||||||||||||||||||||||||
| severity: CRITICAL,HIGH | ||||||||||||||||||||||||
| trivyignores: .github/.trivyignore.yaml | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Trivy scan (high — warn only) | ||||||||||||||||||||||||
| uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 | ||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||
| - name: Evaluate Trivy results | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| if [ ! -f trivy-backend.json ]; then | ||||||||||||||||||||||||
| echo "::error::trivy-backend.json not found — Trivy scan may have failed to produce output" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| echo "## Trivy Scan — Backend" | ||||||||||||||||||||||||
|
greptile-apps[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||
| TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json) | ||||||||||||||||||||||||
| if [ "$TOTAL" -eq 0 ]; then | ||||||||||||||||||||||||
|
Comment on lines
+132
to
+133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add validation for jq output before arithmetic comparison. If 🛡️ Proposed fix to add validation- TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json)
+ TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-backend.json) || {
+ echo "::error::Failed to parse trivy-backend.json"
+ exit 1
+ }
+ if ! [[ "$TOTAL" =~ ^[0-9]+$ ]]; then
+ echo "::error::Invalid vulnerability count: $TOTAL"
+ exit 1
+ fiApply the same pattern to 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| echo "No CRITICAL or HIGH vulnerabilities found." | ||||||||||||||||||||||||
| echo "## Trivy Scan — Backend: No CRITICAL or HIGH vulnerabilities found." >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| TABLE=$(jq -r ' | ||||||||||||||||||||||||
| ["SEVERITY","CVE","PACKAGE","VERSION","TITLE"], | ||||||||||||||||||||||||
| (.Results[]?.Vulnerabilities[]? | | ||||||||||||||||||||||||
| [.Severity, .VulnerabilityID, .PkgName, .InstalledVersion, (.Title // "")[0:60]]) | | ||||||||||||||||||||||||
| @tsv | ||||||||||||||||||||||||
| ' trivy-backend.json | column -t -s$'\t') | ||||||||||||||||||||||||
| echo "$TABLE" | ||||||||||||||||||||||||
| printf '## Trivy Scan — Backend\n```\n%s\n```\n' "$TABLE" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-backend.json) | ||||||||||||||||||||||||
| HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-backend.json) | ||||||||||||||||||||||||
| echo "Critical: $CRITICAL, High: $HIGH" | ||||||||||||||||||||||||
| echo "**Critical: $CRITICAL, High: $HIGH**" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| if [ "$HIGH" -gt 0 ]; then | ||||||||||||||||||||||||
| echo "::warning::Found $HIGH HIGH severity vulnerabilities" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| if [ "$CRITICAL" -gt 0 ]; then | ||||||||||||||||||||||||
| echo "::error::Found $CRITICAL CRITICAL vulnerabilities — failing build" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
greptile-apps[bot] marked this conversation as resolved.
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Upload Trivy report (backend) | ||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | ||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||
| image-ref: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| format: table | ||||||||||||||||||||||||
| exit-code: "0" | ||||||||||||||||||||||||
| severity: HIGH | ||||||||||||||||||||||||
| trivyignores: .github/.trivyignore.yaml | ||||||||||||||||||||||||
| name: trivy-backend-report | ||||||||||||||||||||||||
| path: trivy-backend.json | ||||||||||||||||||||||||
| retention-days: 30 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Grype scan | ||||||||||||||||||||||||
| uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2 | ||||||||||||||||||||||||
|
|
@@ -137,6 +172,16 @@ jobs: | |||||||||||||||||||||||
| severity-cutoff: critical | ||||||||||||||||||||||||
| config: .github/.grype.yaml | ||||||||||||||||||||||||
|
Comment on lines
167
to
173
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Grype scan skipped when Trivy finds CRITICAL vulnerabilities In the old design, the This silently breaks the "two independent scanners" guarantee described in both Adding
Suggested change
The same applies to the Prompt To Fix With AIThis is a comment left during a code review.
Path: .github/workflows/docker.yml
Line: 167-173
Comment:
**Grype scan skipped when Trivy finds CRITICAL vulnerabilities**
In the old design, the `Trivy scan (high — warn only)` step had `continue-on-error: true`, which kept the job in a "success" state so that Grype always ran independently. In the new design, `Evaluate Trivy results` calls `exit 1` when CRITICAL vulnerabilities are found. Because `Grype scan` has no `if` condition it defaults to `if: success()` — so it is skipped entirely whenever Trivy flags a CRITICAL CVE.
This silently breaks the "two independent scanners" guarantee described in both `CLAUDE.md` and `docs/security.md`. If the Grype database contains a CRITICAL CVE that Trivy misses, that finding is only surfaced when Trivy's own scan is clean, which defeats the purpose of running two independent tools.
Adding `if: always()` to the Grype step (and, optionally, the CIS benchmark step) in both jobs restores independent coverage:
```suggestion
- name: Grype scan
if: always()
uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2
with:
image: ${{ steps.scan-ref.outputs.ref }}
fail-build: true
severity-cutoff: critical
config: .github/.grype.yaml
```
The same applies to the `Grype scan` step in the `build-web` job (around line 337).
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # CIS Docker Benchmark v1.6.0 compliance check (uses trivy installed above). | ||||||||||||||||||||||||
| # Informational for now — remove continue-on-error once baseline is clean. | ||||||||||||||||||||||||
| - name: CIS Docker Benchmark (backend) | ||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||
| IMAGE_REF: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| command -v trivy >/dev/null 2>&1 || { echo "::warning::trivy not on PATH, skipping CIS scan"; exit 0; } | ||||||||||||||||||||||||
| trivy image --compliance docker-cis-1.6.0 --format table "$IMAGE_REF" | ||||||||||||||||||||||||
|
Comment on lines
+177
to
+183
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CIS benchmark steps may silently skip on certain runner configurations The step guards with Since the step is already - name: CIS Docker Benchmark (backend)
continue-on-error: true
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
scan-type: image
format: table
exit-code: "0"
compliance: docker-cis-1.6.0Prompt To Fix With AIThis is a comment left during a code review.
Path: .github/workflows/docker.yml
Line: 161-167
Comment:
**CIS benchmark steps may silently skip on certain runner configurations**
The step guards with `command -v trivy` and emits a `::warning::` if trivy isn't on `PATH`. Whether trivy is available depends on the `aquasecurity/trivy-action` implementation detail — recent versions install the binary directly on the runner host, but this isn't guaranteed across all runner environments or action versions.
Since the step is already `continue-on-error: true` (informational), the silent skip is low-risk. However, the same pattern also appears at the web job (line ~318). If you want certainty that the CIS scan always runs, consider using a second `aquasecurity/trivy-action` invocation with `scan-type: image` and appropriate flags instead of relying on the host `trivy` binary:
```yaml
- name: CIS Docker Benchmark (backend)
continue-on-error: true
uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0
with:
image-ref: ${{ steps.scan-ref.outputs.ref }}
scan-type: image
format: table
exit-code: "0"
compliance: docker-cis-1.6.0
```
How can I resolve this? If you propose a fix, please make it concise. |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Push only after scans pass — prevents publishing vulnerable images. | ||||||||||||||||||||||||
| # NOTE: This is a separate build invocation from the scan step. GHA cache | ||||||||||||||||||||||||
| # ensures deterministic layer content, but the manifest digest differs due | ||||||||||||||||||||||||
|
|
@@ -236,24 +281,58 @@ jobs: | |||||||||||||||||||||||
| id: scan-ref | ||||||||||||||||||||||||
| run: echo "ref=ghcr.io/aureliolo/synthorg-web:sha-${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Trivy scan (critical — hard fail) | ||||||||||||||||||||||||
| # Single Trivy run for CRITICAL + HIGH (saves ~12s vs two separate runs). | ||||||||||||||||||||||||
| - name: Trivy scan | ||||||||||||||||||||||||
| uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 | ||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||
| image-ref: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| format: table | ||||||||||||||||||||||||
| exit-code: "1" | ||||||||||||||||||||||||
| severity: CRITICAL | ||||||||||||||||||||||||
| format: json | ||||||||||||||||||||||||
| output: trivy-web.json | ||||||||||||||||||||||||
| exit-code: "0" | ||||||||||||||||||||||||
| severity: CRITICAL,HIGH | ||||||||||||||||||||||||
| trivyignores: .github/.trivyignore.yaml | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Trivy scan (high — warn only) | ||||||||||||||||||||||||
| uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.0 | ||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||
| - name: Evaluate Trivy results | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| if [ ! -f trivy-web.json ]; then | ||||||||||||||||||||||||
| echo "::error::trivy-web.json not found — Trivy scan may have failed to produce output" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| echo "## Trivy Scan — Web" | ||||||||||||||||||||||||
| TOTAL=$(jq '[.Results[]?.Vulnerabilities[]?] | length' trivy-web.json) | ||||||||||||||||||||||||
| if [ "$TOTAL" -eq 0 ]; then | ||||||||||||||||||||||||
|
Comment on lines
+302
to
+303
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same jq validation issue as backend section. Apply the same numeric validation fix as recommended for the backend section (lines 132-133). The 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
| echo "No CRITICAL or HIGH vulnerabilities found." | ||||||||||||||||||||||||
| echo "## Trivy Scan — Web: No CRITICAL or HIGH vulnerabilities found." >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| else | ||||||||||||||||||||||||
| TABLE=$(jq -r ' | ||||||||||||||||||||||||
| ["SEVERITY","CVE","PACKAGE","VERSION","TITLE"], | ||||||||||||||||||||||||
| (.Results[]?.Vulnerabilities[]? | | ||||||||||||||||||||||||
| [.Severity, .VulnerabilityID, .PkgName, .InstalledVersion, (.Title // "")[0:60]]) | | ||||||||||||||||||||||||
| @tsv | ||||||||||||||||||||||||
| ' trivy-web.json | column -t -s$'\t') | ||||||||||||||||||||||||
| echo "$TABLE" | ||||||||||||||||||||||||
| printf '## Trivy Scan — Web\n```\n%s\n```\n' "$TABLE" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| CRITICAL=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "CRITICAL")] | length' trivy-web.json) | ||||||||||||||||||||||||
| HIGH=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity == "HIGH")] | length' trivy-web.json) | ||||||||||||||||||||||||
| echo "Critical: $CRITICAL, High: $HIGH" | ||||||||||||||||||||||||
| echo "**Critical: $CRITICAL, High: $HIGH**" >> "$GITHUB_STEP_SUMMARY" | ||||||||||||||||||||||||
| if [ "$HIGH" -gt 0 ]; then | ||||||||||||||||||||||||
| echo "::warning::Found $HIGH HIGH severity vulnerabilities" | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
| if [ "$CRITICAL" -gt 0 ]; then | ||||||||||||||||||||||||
| echo "::error::Found $CRITICAL CRITICAL vulnerabilities — failing build" | ||||||||||||||||||||||||
| exit 1 | ||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Upload Trivy report (web) | ||||||||||||||||||||||||
| if: always() | ||||||||||||||||||||||||
| uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7 | ||||||||||||||||||||||||
| with: | ||||||||||||||||||||||||
| image-ref: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| format: table | ||||||||||||||||||||||||
| exit-code: "0" | ||||||||||||||||||||||||
| severity: HIGH | ||||||||||||||||||||||||
| trivyignores: .github/.trivyignore.yaml | ||||||||||||||||||||||||
| name: trivy-web-report | ||||||||||||||||||||||||
| path: trivy-web.json | ||||||||||||||||||||||||
| retention-days: 30 | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: Grype scan | ||||||||||||||||||||||||
| uses: anchore/scan-action@7037fa011853d5a11690026fb85feee79f4c946c # v7.3.2 | ||||||||||||||||||||||||
|
|
@@ -263,6 +342,14 @@ jobs: | |||||||||||||||||||||||
| severity-cutoff: critical | ||||||||||||||||||||||||
| config: .github/.grype.yaml | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - name: CIS Docker Benchmark (web) | ||||||||||||||||||||||||
| continue-on-error: true | ||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||
| IMAGE_REF: ${{ steps.scan-ref.outputs.ref }} | ||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||
| command -v trivy >/dev/null 2>&1 || { echo "::warning::trivy not on PATH, skipping CIS scan"; exit 0; } | ||||||||||||||||||||||||
| trivy image --compliance docker-cis-1.6.0 --format table "$IMAGE_REF" | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Push only after scans pass — prevents publishing vulnerable images. | ||||||||||||||||||||||||
| # NOTE: Separate build invocation; GHA cache ensures deterministic layers, | ||||||||||||||||||||||||
| # but manifest digest differs due to SBOM + provenance attestation layers. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.