Files
Charon/docs/reports/qa_report.md
GitHub Actions 5bafd92edf fix: supply slack webhook token in handler create sub-tests
The slack sub-tests in TestDiscordOnly_CreateRejectsNonDiscord and
TestBlocker3_CreateProviderRejectsNonDiscordWithSecurityEvents were
omitting the required token field from their request payloads.
CreateProvider enforces that Slack providers must have a non-empty
token (the webhook URL) at creation time. Without it the service
returns "slack webhook URL is required", which the handler does not
classify as a 400 validation error, so it falls through to 500.

Add a token field to each test struct, populate it for the slack
case with a valid-format Slack webhook URL, and use
WithSlackURLValidator to bypass the real format check in unit tests —
matching the pattern used in all existing service-level Slack tests.
2026-03-15 15:17:23 +00:00

12 KiB
Raw Blame History

QA Audit Report — Slack Sub-Test Fixes

Date: 2026-03-15 Branch: feature/beta-release Commit: 6e4294dcfix: validate Slack webhook URL at provider create/update time Scope: Two handler test files + service test + notification_service.go (Slack URL validation) Reviewer: QA Security Agent


Overall Verdict: PASS

All quality and security gates pass. The two test-only fixes are safe, coverage is maintained above threshold, and no security regressions were introduced.


Changes Under Review

File Type Description
backend/internal/services/notification_service.go Production Added WithSlackURLValidator option and Slack URL validation at create/update
backend/internal/services/notification_service_test.go Test Added Slack sub-tests using WithSlackURLValidator bypass
backend/internal/api/handlers/notification_provider_discord_only_test.go Test Added token field and WithSlackURLValidator to fix failing slack sub-test
backend/internal/api/handlers/notification_provider_blocker3_test.go Test Added token field and WithSlackURLValidator to fix failing slack sub-test

Gate Summary

# Gate Result Details
1 Backend test suite — pass/fail PASS 0 failures across all packages
2 Line coverage ≥87% PASS 88.2% line / 87.9% statement
3 Patch coverage ≥90% (overall) PASS 95.1% on changed lines
4 go vet PASS 0 issues
5 golangci-lint (fast) PASS 0 issues
6 Pre-commit hooks (lefthook) PASS All 6 hooks pass
7 Trivy filesystem scan (Critical/High) PASS 0 findings
8 GORM security scan (Critical/High) PASS 0 findings
9 Security review of WithSlackURLValidator PASS Production defaults are safe; informational note only

1. Backend Test Suite

Command: bash /projects/Charon/.github/skills/scripts/skill-runner.sh test-backend-coverage

Metric Result Threshold
Test failures 0 0
Statement coverage 87.9% ≥87%
Line coverage 88.2% ≥87%

All packages returned ok. No FAIL entries. Selected package breakdown:

Package Coverage
internal/api/handlers 86.3%
internal/api/middleware 97.2%
internal/api/routes 89.5%
internal/caddy 96.8%
internal/cerberus 93.8%
internal/metrics 100.0%
internal/models 97.3%
internal/services included in global

2. Patch Coverage (Local Patch Report)

Command: bash /projects/Charon/scripts/local-patch-report.sh

Scope Changed Lines Covered Patch Coverage Threshold
Overall 81 77 95.1% ≥90%
Backend 75 71 94.7% ≥85%
Frontend 6 6 100.0% ≥85%

4 uncovered changed lines in notification_service.go at lines 477478 and 481482. These are error-handling branches for json.Marshal and bytes.Buffer.Write in Slack payload normalization — conditions requiring OOM-class failures and effectively unreachable in practice. Not a coverage concern.


3. Pre-Commit Hooks

Commands: lefthook run pre-commit; go vet ./...; golangci-lint

Hook Result
end-of-file-fixer PASS
trailing-whitespace PASS
check-yaml PASS
actionlint PASS
dockerfile-check PASS
shellcheck PASS
go vet ./... (manual) PASS — 0 issues
golangci-lint v2.9.0 (manual) PASS — 0 issues

Go and frontend hooks were skipped by lefthook (no staged files). Both were run manually and returned clean results.


4. Trivy Filesystem Scan

Command: bash /projects/Charon/.github/skills/scripts/skill-runner.sh security-scan-trivy

Scanners: vuln,secret. Severity filter: CRITICAL,HIGH,MEDIUM.

Target Type Vulnerabilities Secrets
backend/go.mod gomod 0
frontend/package-lock.json npm 0
package-lock.json npm 0
playwright/.auth/user.json text 0

Result: 0 findings.


5. GORM Security Scan

Command: bash /projects/Charon/.github/skills/scripts/skill-runner.sh security-scan-gorm

Scanned 41 Go files (2,253 lines).

Severity Count
CRITICAL 0
HIGH 0
MEDIUM 0
INFO 2 (pre-existing, informational only)

Result: 0 actionable issues.


6. Security Review: WithSlackURLValidator

The new WithSlackURLValidator functional option exposes a test-injectable hook in NotificationService.

Concern Assessment
SSRF via production bypass Not a risk. NewNotificationService always defaults to validateSlackWebhookURL. The option must be explicitly passed at construction time.
Allowlist strength Regex ^https://hooks\.slack\.com/services/T[A-Za-z0-9_-]+/B[A-Za-z0-9_-]+/[A-Za-z0-9_-]+$ — pins to HTTPS, exact domain, and enforced path structure. Robust against SSRF.
Test bypass scope Only used in *_test.go files. Comment states: "Intended for use in tests that need to bypass real URL validation without mutating shared state."
Exported from production package LOW / Informational. The option is exported from services with no //go:build test build-tag guard. It could theoretically be called from production code, but there is no evidence of misuse and the default is safe. Consider adding a build-tag guard in a future cleanup pass if stricter separation is desired.

7. Scope Exclusions

Check Excluded Justification
E2E Playwright tests Yes No UI or routing changes
CodeQL Yes No Go or JavaScript semantic changes
Docker image scan Yes No Dockerfile changes
Frontend unit coverage N/A Frontend patch coverage 100%

Change Summary

File Change Line
scripts/cerberus_integration.sh Add -e PORT=80 to docker run ... mccutchen/go-httpbin L174
scripts/waf_integration.sh Add -e PORT=80 to docker run ... mccutchen/go-httpbin L167
scripts/rate_limit_integration.sh Add -e PORT=80 to docker run ... mccutchen/go-httpbin L187
scripts/coraza_integration.sh Add -e PORT=80 to docker run ... mccutchen/go-httpbin L158
scripts/crowdsec_startup_test.sh Replace curl -sf with wget -qO - in docker exec L179
scripts/diagnose-test-env.sh Replace curl -sf with wget -qO /dev/null in docker exec L104

Gate Summary

# Gate Result Details
1 Syntax Validation (bash -n) PASS All 6 scripts parse cleanly
2 ShellCheck (error severity) PASS 0 errors; matches lefthook --severity=error
3 ShellCheck (all severities) PASS No findings on any modified line; all findings pre-existing
4 Pre-commit Hooks (lefthook) PASS All 6 hooks passed (shellcheck, actionlint, yaml, whitespace, eof, dockerfile)
5 Verification: go-httpbin PORT PASS 4/4 docker run lines have -e PORT=80
6 Verification: docker exec curl PASS 0 executed curl calls; 2 echo-only references (hints)
7 Security Review PASS No secrets, credentials, injection vectors, or Gotify tokens
8 Trivy Filesystem Scan PASS 0 secrets, 0 misconfigurations

1. Syntax Validation (bash -n)

Script Result
scripts/cerberus_integration.sh PASS
scripts/waf_integration.sh PASS
scripts/rate_limit_integration.sh PASS
scripts/coraza_integration.sh PASS
scripts/crowdsec_startup_test.sh PASS
scripts/diagnose-test-env.sh PASS

2. ShellCheck

At error severity (--severity=error, matching lefthook pre-commit)

Result: PASS — Zero errors across all 6 scripts. Exit code 0.

At default severity (full informational audit)

Exit code 1 (findings present, all note or warning severity).

Script Findings Severity On Modified Lines?
cerberus_integration.sh 2× SC2086 (unquoted variable) note No (L204, L219)
waf_integration.sh ~30× SC2317 (unreachable code in trap), 3× SC2086 note No
rate_limit_integration.sh 9× SC2086 note No
coraza_integration.sh 10× SC2086, 2× SC2034 (unused variable) note/warning No
crowdsec_startup_test.sh ~10× SC2317, 1× SC2086 note No
diagnose-test-env.sh 1× SC2034 (unused variable) warning No

No ShellCheck findings on any of the 6 modified lines. All findings are pre-existing.


3. Pre-commit Hooks (lefthook)

Ran lefthook run pre-commit:

Hook Result Duration
check-yaml PASS 1.93s
actionlint PASS 4.36s
end-of-file-fixer PASS 9.23s
trailing-whitespace PASS 9.49s
dockerfile-check PASS 10.41s
shellcheck PASS 11.24s

Hooks for Go, TypeScript, and Semgrep correctly skipped (no matching files).


4. Verification Greps

4a. All mccutchen/go-httpbin docker run instances have -e PORT=80

scripts/cerberus_integration.sh:174:  docker run ... -e PORT=80 mccutchen/go-httpbin
scripts/waf_integration.sh:167:       docker run ... -e PORT=80 mccutchen/go-httpbin
scripts/rate_limit_integration.sh:187:docker run ... -e PORT=80 mccutchen/go-httpbin
scripts/coraza_integration.sh:158:    docker run ... -e PORT=80 mccutchen/go-httpbin

Remaining mccutchen/go-httpbin matches are docker pull lines (no -e PORT needed).

Result: PASS — 4/4 confirmed.

4b. Zero executed docker exec ... curl calls

Only 2 matches found in scripts/verify_crowdsec_app_config.sh (L9495) — both inside echo statements (user hint text, not executed). Confirmed by manual review.

Result: PASS — 0 executed docker exec ... curl calls.


5. Security Review

Check Result Notes
Secrets/credentials in diff PASS `git diff
Gotify tokens PASS grep -rn "Gotify|gotify|token=" across all 6 scripts — no matches
Injection vectors PASS -e PORT=80 is a static literal; no user-controlled input flows into new code
Command injection PASS wget -qO flags are hardcoded; no interpolated user input
SSRF N/A URLs are internal container addresses (127.0.0.1, localhost) in CI-only scripts
Sensitive data in logs PASS No new log/echo statements added
URL query parameters PASS No tokenized URLs (e.g., ?token=...) in changed or adjacent code

6. Trivy Filesystem Scan

Scanners: secret,misconfig. Severity filter: CRITICAL,HIGH,MEDIUM.

Target Type Secrets Misconfigurations
backend/go.mod gomod
frontend/package-lock.json npm
package-lock.json npm
Dockerfile dockerfile 0
playwright/.auth/user.json text 0

Result: 0 findings. Exit code 0.


7. Scope Exclusions

Check Excluded? Justification
E2E Playwright tests Yes Scripts are CI-only; no UI changes
Backend unit coverage Yes No Go code changes
Frontend unit coverage Yes No TypeScript/React changes
Docker image scan Yes No Dockerfile or image changes
CodeQL Yes No Go or JavaScript changes
GORM security scan Yes No model/database changes
Local patch coverage report Yes No application code; scripts not coverage-tracked

8. Pre-existing Issues (Not Introduced by This Change)

Category Count Scripts Affected Risk
SC2086 (unquoted variables) ~25 All 6 Low — CI-controlled variables
SC2317 (unreachable code) ~40 waf, crowdsec None — trap cleanup functions (ShellCheck false positive)
SC2034 (unused variables) 3 coraza, diagnose Low — may be planned for future use

Remaining Validation (CI)

The integration scripts cannot be executed locally without a built charon:local image and Docker network. Full end-to-end validation will occur when the PR triggers CI:

  • .github/workflows/cerberus-integration.yml
  • .github/workflows/waf-integration.yml
  • .github/workflows/rate-limit-integration.yml
  • .github/workflows/crowdsec-integration.yml