fix: implement security severity policy and enhance CodeQL checks for blocking findings
This commit is contained in:
69
.github/workflows/codeql.yml
vendored
69
.github/workflows/codeql.yml
vendored
@@ -122,10 +122,28 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2016
|
||||
EFFECTIVE_LEVELS_JQ='[
|
||||
.runs[] as $run
|
||||
| $run.results[]
|
||||
| . as $result
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| ((
|
||||
$result.level
|
||||
// (if (($result.ruleIndex | type) == "number") then ($rules[$result.ruleIndex].defaultConfiguration.level // empty) else empty end)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| (.defaultConfiguration.level // empty)
|
||||
][0] // empty)
|
||||
// ""
|
||||
) | ascii_downcase)
|
||||
]'
|
||||
|
||||
echo "Found SARIF file: $SARIF_FILE"
|
||||
ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE")
|
||||
WARNING_COUNT=$(jq '[.runs[].results[] | select(.level == "warning")] | length' "$SARIF_FILE")
|
||||
NOTE_COUNT=$(jq '[.runs[].results[] | select(.level == "note")] | length' "$SARIF_FILE")
|
||||
ERROR_COUNT=$(jq -r "${EFFECTIVE_LEVELS_JQ} | map(select(. == \"error\")) | length" "$SARIF_FILE")
|
||||
WARNING_COUNT=$(jq -r "${EFFECTIVE_LEVELS_JQ} | map(select(. == \"warning\")) | length" "$SARIF_FILE")
|
||||
NOTE_COUNT=$(jq -r "${EFFECTIVE_LEVELS_JQ} | map(select(. == \"note\")) | length" "$SARIF_FILE")
|
||||
|
||||
{
|
||||
echo "**Findings:**"
|
||||
@@ -135,14 +153,32 @@ jobs:
|
||||
echo ""
|
||||
|
||||
if [ "$ERROR_COUNT" -gt 0 ]; then
|
||||
echo "❌ **CRITICAL:** High-severity security issues found!"
|
||||
echo "❌ **BLOCKING:** CodeQL error-level security issues found"
|
||||
echo ""
|
||||
echo "### Top Issues:"
|
||||
echo '```'
|
||||
jq -r '.runs[].results[] | select(.level == "error") | "\(.ruleId): \(.message.text)"' "$SARIF_FILE" | head -5
|
||||
# shellcheck disable=SC2016
|
||||
jq -r '
|
||||
.runs[] as $run
|
||||
| $run.results[]
|
||||
| . as $result
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| ((
|
||||
$result.level
|
||||
// (if (($result.ruleIndex | type) == "number") then ($rules[$result.ruleIndex].defaultConfiguration.level // empty) else empty end)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| (.defaultConfiguration.level // empty)
|
||||
][0] // empty)
|
||||
// ""
|
||||
) | ascii_downcase) as $effectiveLevel
|
||||
| select($effectiveLevel == "error")
|
||||
| "\($effectiveLevel): \($result.ruleId // \"<unknown-rule>\"): \($result.message.text)"
|
||||
' "$SARIF_FILE" | head -5
|
||||
echo '```'
|
||||
else
|
||||
echo "✅ No high-severity issues found"
|
||||
echo "✅ No blocking CodeQL issues found"
|
||||
fi
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
@@ -169,9 +205,26 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ERROR_COUNT=$(jq '[.runs[].results[] | select(.level == "error")] | length' "$SARIF_FILE")
|
||||
# shellcheck disable=SC2016
|
||||
ERROR_COUNT=$(jq -r '[
|
||||
.runs[] as $run
|
||||
| $run.results[]
|
||||
| . as $result
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| ((
|
||||
$result.level
|
||||
// (if (($result.ruleIndex | type) == "number") then ($rules[$result.ruleIndex].defaultConfiguration.level // empty) else empty end)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| (.defaultConfiguration.level // empty)
|
||||
][0] // empty)
|
||||
// ""
|
||||
) | ascii_downcase) as $effectiveLevel
|
||||
| select($effectiveLevel == "error")
|
||||
] | length' "$SARIF_FILE")
|
||||
|
||||
if [ "$ERROR_COUNT" -gt 0 ]; then
|
||||
echo "::error::CodeQL found $ERROR_COUNT high-severity security issues. Fix before merging."
|
||||
echo "::error::CodeQL found $ERROR_COUNT blocking findings (effective-level=error). Fix before merging. Policy: .github/security-severity-policy.yml"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
114
.github/workflows/nightly-build.yml
vendored
114
.github/workflows/nightly-build.yml
vendored
@@ -355,10 +355,116 @@ jobs:
|
||||
sarif_file: 'trivy-nightly.sarif'
|
||||
category: 'trivy-nightly'
|
||||
|
||||
- name: Check for critical CVEs
|
||||
- name: Security severity policy summary
|
||||
run: |
|
||||
if grep -q "CRITICAL" trivy-nightly.sarif; then
|
||||
echo "❌ Critical vulnerabilities found in nightly build"
|
||||
{
|
||||
echo "## 🔐 Nightly Supply Chain Severity Policy"
|
||||
echo ""
|
||||
echo "- Blocking: Critical, High"
|
||||
echo "- Medium: non-blocking by default (report + triage SLA)"
|
||||
echo "- Policy file: .github/security-severity-policy.yml"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Check for Critical/High CVEs
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
jq -e . trivy-nightly.sarif >/dev/null
|
||||
|
||||
CRITICAL_COUNT=$(jq -r '
|
||||
[
|
||||
.runs[] as $run
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| $run.results[]?
|
||||
| . as $result
|
||||
| (
|
||||
(
|
||||
if (($result.ruleIndex | type) == "number") then
|
||||
($rules[$result.ruleIndex].properties["security-severity"] // empty)
|
||||
else
|
||||
empty
|
||||
end
|
||||
)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| .properties["security-severity"]
|
||||
][0] // empty)
|
||||
// empty
|
||||
) as $securitySeverity
|
||||
| (try ($securitySeverity | tonumber) catch empty) as $score
|
||||
| select($score != null and $score >= 9.0)
|
||||
] | length
|
||||
' trivy-nightly.sarif)
|
||||
|
||||
HIGH_COUNT=$(jq -r '
|
||||
[
|
||||
.runs[] as $run
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| $run.results[]?
|
||||
| . as $result
|
||||
| (
|
||||
(
|
||||
if (($result.ruleIndex | type) == "number") then
|
||||
($rules[$result.ruleIndex].properties["security-severity"] // empty)
|
||||
else
|
||||
empty
|
||||
end
|
||||
)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| .properties["security-severity"]
|
||||
][0] // empty)
|
||||
// empty
|
||||
) as $securitySeverity
|
||||
| (try ($securitySeverity | tonumber) catch empty) as $score
|
||||
| select($score != null and $score >= 7.0 and $score < 9.0)
|
||||
] | length
|
||||
' trivy-nightly.sarif)
|
||||
|
||||
MEDIUM_COUNT=$(jq -r '
|
||||
[
|
||||
.runs[] as $run
|
||||
| ($run.tool.driver.rules // []) as $rules
|
||||
| $run.results[]?
|
||||
| . as $result
|
||||
| (
|
||||
(
|
||||
if (($result.ruleIndex | type) == "number") then
|
||||
($rules[$result.ruleIndex].properties["security-severity"] // empty)
|
||||
else
|
||||
empty
|
||||
end
|
||||
)
|
||||
// ([
|
||||
$rules[]?
|
||||
| select((.id // "") == ($result.ruleId // ""))
|
||||
| .properties["security-severity"]
|
||||
][0] // empty)
|
||||
// empty
|
||||
) as $securitySeverity
|
||||
| (try ($securitySeverity | tonumber) catch empty) as $score
|
||||
| select($score != null and $score >= 4.0 and $score < 7.0)
|
||||
] | length
|
||||
' trivy-nightly.sarif)
|
||||
|
||||
{
|
||||
echo "- Structured SARIF counts: CRITICAL=${CRITICAL_COUNT}, HIGH=${HIGH_COUNT}, MEDIUM=${MEDIUM_COUNT}"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
if [ "$CRITICAL_COUNT" -gt 0 ]; then
|
||||
echo "❌ Critical vulnerabilities found in nightly build (${CRITICAL_COUNT})"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ No critical vulnerabilities found"
|
||||
|
||||
if [ "$HIGH_COUNT" -gt 0 ]; then
|
||||
echo "❌ High vulnerabilities found in nightly build (${HIGH_COUNT})"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$MEDIUM_COUNT" -gt 0 ]; then
|
||||
echo "::warning::Medium vulnerabilities found in nightly build (${MEDIUM_COUNT}). Non-blocking by policy; triage with SLA per .github/security-severity-policy.yml"
|
||||
fi
|
||||
|
||||
echo "✅ No Critical/High vulnerabilities found"
|
||||
|
||||
21
.github/workflows/quality-checks.yml
vendored
21
.github/workflows/quality-checks.yml
vendored
@@ -18,6 +18,27 @@ env:
|
||||
GOTOOLCHAIN: auto
|
||||
|
||||
jobs:
|
||||
auth-route-protection-contract:
|
||||
name: Auth Route Protection Contract
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.sha }}
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
cache-dependency-path: backend/go.sum
|
||||
|
||||
- name: Run auth protection contract tests
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd backend
|
||||
go test ./internal/api/routes -run 'TestRegister_StateChangingRoutesRequireAuthentication|TestRegister_StateChangingRoutesDenyByDefaultWithExplicitAllowlist|TestRegister_AuthenticatedRoutes' -count=1 -v
|
||||
|
||||
codecov-trigger-parity-guard:
|
||||
name: Codecov Trigger/Comment Parity Guard
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
32
.github/workflows/supply-chain-pr.yml
vendored
32
.github/workflows/supply-chain-pr.yml
vendored
@@ -337,6 +337,27 @@ jobs:
|
||||
echo " Low: ${LOW_COUNT}"
|
||||
echo " Total: ${TOTAL_COUNT}"
|
||||
|
||||
- name: Security severity policy summary
|
||||
if: steps.set-target.outputs.image_name != ''
|
||||
run: |
|
||||
CRITICAL_COUNT="${{ steps.vuln-summary.outputs.critical_count }}"
|
||||
HIGH_COUNT="${{ steps.vuln-summary.outputs.high_count }}"
|
||||
MEDIUM_COUNT="${{ steps.vuln-summary.outputs.medium_count }}"
|
||||
|
||||
{
|
||||
echo "## 🔐 Supply Chain Severity Policy"
|
||||
echo ""
|
||||
echo "- Blocking: Critical, High"
|
||||
echo "- Medium: non-blocking by default (report + triage SLA)"
|
||||
echo "- Policy file: .github/security-severity-policy.yml"
|
||||
echo ""
|
||||
echo "Current scan counts: Critical=${CRITICAL_COUNT}, High=${HIGH_COUNT}, Medium=${MEDIUM_COUNT}"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
if [[ "${MEDIUM_COUNT}" -gt 0 ]]; then
|
||||
echo "::warning::${MEDIUM_COUNT} medium vulnerabilities found. Non-blocking by policy; create/maintain triage issue with SLA per .github/security-severity-policy.yml"
|
||||
fi
|
||||
|
||||
- name: Upload SARIF to GitHub Security
|
||||
if: steps.check-artifact.outputs.artifact_found == 'true'
|
||||
uses: github/codeql-action/upload-sarif@89a39a4e59826350b863aa6b6252a07ad50cf83e # v4
|
||||
@@ -433,10 +454,11 @@ jobs:
|
||||
|
||||
echo "✅ PR comment posted"
|
||||
|
||||
- name: Fail on critical vulnerabilities
|
||||
- name: Fail on Critical/High vulnerabilities
|
||||
if: steps.set-target.outputs.image_name != ''
|
||||
run: |
|
||||
CRITICAL_COUNT="${{ steps.vuln-summary.outputs.critical_count }}"
|
||||
HIGH_COUNT="${{ steps.vuln-summary.outputs.high_count }}"
|
||||
|
||||
if [[ "${CRITICAL_COUNT}" -gt 0 ]]; then
|
||||
echo "🚨 Found ${CRITICAL_COUNT} CRITICAL vulnerabilities!"
|
||||
@@ -444,4 +466,10 @@ jobs:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ No critical vulnerabilities found"
|
||||
if [[ "${HIGH_COUNT}" -gt 0 ]]; then
|
||||
echo "🚨 Found ${HIGH_COUNT} HIGH vulnerabilities!"
|
||||
echo "Please review the vulnerability report and address high severity issues before merging."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ No Critical/High vulnerabilities found"
|
||||
|
||||
Reference in New Issue
Block a user