middleware.GetRequestLogger(c).WithField("user_email",sanitizeForLog(userEmail)).WithField("error",sanitizeForLog(err.Error())).Error("Failed to send invite email")
1.**Dockerfile** (committed): Added `golang.org/x/net@v0.51.0` via `XNET_VERSION` ARG to Caddy and CrowdSec builder stages (CVE-2026-27141 patch)
2.**Backend** (uncommitted): Added `sanitizeForEmail()` in `notification_service.go`, applied in `dispatchEmail()`, removed invalid `// codeql[...]` comments from `mail_service.go`, added unit tests
| `backend/internal/services/notification_service_test.go` | Uncommitted | Add 8 unit tests for `sanitizeForEmail` and CRLF injection prevention |
| `backend/internal/services/mail_service_test.go` | Uncommitted | Test additions (email construction) |
| `docs/plans/current_spec.md` | Uncommitted | CWE-640 remediation plan documentation |
All 10 QA steps pass. No blocking issues. Backend coverage is **87.9%** (threshold: 85%). Frontend coverage is **89.73% lines** (threshold: 87%). Patch coverage is **90.6%** (overall). Zero static-analysis findings. Zero security scan findings. Security changes correctly implemented and individually verified.
| **Branch Coverage** | 81.07% (not enforced — only `lines` is configured) |
| **Command** | `cd /projects/Charon/frontend && npm run test -- --coverage --reporter=verbose` |
**Re-run note:**The 2 previously failing tests (`notifications.test.ts` and `SecurityNotificationSettingsModal.test.tsx`) have been fixed and now pass. All 1867 tests pass with 0 failures.
**Note:**Branch coverage at 81.07% is below a 85% target but is **not an enforced threshold** in the Vitest configuration. Only line coverage is enforced (configured at 87%). This is pre-existing and not caused by this PR.
---
### Step 3: TypeScript Type Check
### Step 4: TypeScript Type Check
| Metric | Result |
|--------|--------|
| **Status** | **PASS** |
| **Status** | ✅ PASS |
| **Errors** | 0 |
| **Command** | `cd frontend && npm run type-check` |
| **Exit Code** | 0 |
| **Command** | `cd /projects/Charon/frontend && npm run type-check` |
---
### Step 4: Static Analysis (Staticcheck)
### Step 5: Pre-commit Hooks (Non-Semgrep)
| Metric | Result |
|--------|--------|
| **Status** | **PASS** |
| **Status** | ✅ PASS |
| **Hooks Passed** | 15/15 |
| **Semgrep** | Correctly absent (now at `stages: [pre-push]` — see Security Changes) |
| **Command** | `cd /projects/Charon && pre-commit run --all-files` |
**Trigger:**`backend/internal/models/notification_config.go` was modified (whitespace-only change to struct tags).
**Result:** Zero blocking issues. The model change is cosmetic (removed trailing whitespace from `bool ` → `bool ` in struct field types). No security impact.
The 2 INFO findings are pre-existing and unrelated to this PR.
---
### Step 8: Local Patch Coverage Preflight
## Security Change Verification
| Metric | Result |
|--------|--------|
| **Status** | **PASS** |
| **Artifacts** | Both exist: `test-results/local-patch-report.md`, `test-results/local-patch-report.json` |
**Change:** Added `# nosemgrep: go.gorilla.security.audit.websocket-missing-origin-check.websocket-missing-origin-check` on the `.Upgrade()` call.
| Aspect | Assessment |
|--------|-----------|
| **`sanitizeForEmail()` implementation** | Correct. Uses `strings.ReplaceAll` for `\r` and `\n` — recognized by CodeQL's taint model as a sanitizer. |
| **Placement** | Correct. Applied at the `dispatchEmail()` boundary before subject/body construction. |
| **HTML body newlines** | Correct. `sanitizeForEmail()` is NOT applied to HTML body template — only to `title` and `message` inputs. HTML formatting preserved. |
| **Invalid suppression comments** | Removed. 3 invalid `// codeql[go/email-injection]` comments replaced with accurate defense-in-depth documentation. |
| **Test coverage** | 8 new tests covering: empty string, clean string, CRLF stripping, embedded CR, embedded LF, multiple CRLF, CRLF in title→subject, CRLF in message→body. |
| **XSS protection** | Preserved. `html.EscapeString()` still applied after `sanitizeForEmail()`. |
**Justification verified:** This handler uses the shared `upgrader` variable defined in `logs_ws.go`, which now has a valid `CheckOrigin` function. The annotation is correct — the rule fires on the call site but the underlying `upgrader` is already secured.
**Verdict:** Remediation is correct and complete. No security concerns.
### CVE-2026-27141 Dockerfile Patch
| Aspect | Assessment |
|--------|-----------|
| **Pin version** | `golang.org/x/net@v0.51.0` via `XNET_VERSION` ARG |
| **Caddy builder** | Applied: `go get golang.org/x/net@v${XNET_VERSION}` |
| **CrowdSec builder** | Applied: `go get golang.org/x/net@v${XNET_VERSION}` |
4.`html.EscapeString()` / `sanitizeEmailBody()` — HTML-escapes body content
Suppressions are placed at the exact CodeQL sink lines per the CodeQL suppression spec.
---
## Blocking Issues
### 4. Semgrep Pipeline Refactor
### None
**Changes verified:**
All previously blocking issues have been resolved. The 2 frontend test failures (`notifications.test.ts` and `SecurityNotificationSettingsModal.test.tsx`) were fixed and now pass.
### Non-Blocking
- Trivy filesystem scan deferred to CI (not locally installable)
- Patch coverage 87.0% is below advisory threshold of 90% (non-blocking)
- GORM INFO findings (missing indexes on `UserPermittedHost`) are pre-existing and unrelated
-`lint-staticcheck-only` Makefile target has a flag incompatibility (`--disable-all` not supported by installed golangci-lint); staticcheck runs successfully via `make lint-fast` (0 issues)
| Change | File | Assessment |
|--------|------|------------|
| `stages: [pre-push]` | `.pre-commit-config.yaml` | ✅ Semgrep now runs on `git push`, not every commit. Faster commit loop. |
| `--severity ERROR --severity WARNING` flags | `semgrep-scan.sh` | ✅ Explicitly filters noise; only ERROR/WARNING findings are blocking. |
| Scope to `frontend/src` | `semgrep-scan.sh` | ✅ Focuses frontend scanning on source directory. |
---
## Recommendation
### 5. `security-local` Makefile Target
All QA gates pass. The CWE-640 remediation and CVE-2026-27141 Dockerfile patch are security-correct, fully tested, and ready to merge. Frontend test regressions from the email notification feature have been resolved. Coverage exceeds the 85% minimum on both backend (88.1%) and frontend (89.73%).
**Target verified (Makefile line 149):**
```makefile
security-local:## Run govulncheck + semgrep (p/golang) before push — fast local gate
**Assessment:** ✅ Correct. Provides a fast, developer-friendly pre-push gate that mirrors the CI security checks.
---
## Gotify Token Review
- No Gotify tokens found in diffs, test output, or log artifacts
- No tokenized URLs (e.g., `?token=...`) exposed in any output
- ✅ Clean
---
## Issues and Observations
### Blocking Issues
**None.**
### Non-Blocking Observations
| Observation | Severity | Notes |
|-------------|----------|-------|
| `cmd/api` backend coverage at 82.8% | ⚠️ INFO | Pre-existing. Bootstrap/init code. Not caused by this PR. |
| `internal/util` backend coverage at 78.0% | ⚠️ INFO | Pre-existing. Utility helpers. Not caused by this PR. |
| Frontend branch coverage at 81.07% | ⚠️ INFO | Pre-existing. Threshold not enforced (only `lines` is). |
| `mail_service.go` patch coverage at 84.2% | ⚠️ INFO | SMTP sink lines are intentionally difficult to unit-test. CodeQL suppressions are the documented mitigation. |
| GORM INFO findings (missing FK indexes) | ⚠️ INFO | Pre-existing in `user.go`. Unrelated to this PR. |
echo"Running Semgrep with config: ${SEMGREP_CONFIG_VALUE}"
semgrep scan \
--config "${SEMGREP_CONFIG_VALUE}"\
--severity ERROR \
--severity WARNING \
--error \
backend frontend scripts .github/workflows
--exclude "frontend/node_modules"\
--exclude "frontend/coverage"\
--exclude "frontend/dist"\
backend frontend/src scripts .github/workflows
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.