Skip to content

fix(security): strengthen file path safety checks in system administration handler - #1216

Merged
Wikid82 merged 7 commits into
developmentfrom
fix/codeql
Aug 4, 2026
Merged

fix(security): strengthen file path safety checks in system administration handler#1216
Wikid82 merged 7 commits into
developmentfrom
fix/codeql

Conversation

@Wikid82

@Wikid82 Wikid82 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • CodeQL flagged 4 go/path-injection findings in system_permissions_handler.go's admin-only, root-required, single-container-mode-only permission-repair endpoint — all sharing the same req.Paths taint source, hitting os.Lstat (×2), os.Chown, and os.Chmod.
  • The path was already fully confined (absolute-only, no .., allowlist-checked, symlink-rejected) before every sink, but CodeQL's Go sanitizer only recognizes a direct, same-function, non-loop strings.HasPrefix(taintedVar, ...) call as a barrier — not a call routed through a separate helper. This is a scanner-legibility hardening fix, not a fix for a previously-exploitable bug.
  • Introduced firstAllowlistPrefix + inline strings.HasPrefix guards at each sink, in the sink's own function, with zero change to existing validation semantics or the endpoint's external behavior.
  • Removed a dead intermediate helper (isWithinAllowlistBounds) once it was confirmed unused in production after the mechanism above proved to be the one CodeQL actually recognizes.
  • Filed the unrelated go/cookie-secure-not-set suppression-comment bug (auth_handler.go:191, not honored by CodeQL) as a separate tracked issue rather than bundling it here.
  • Archived a superseded, unrelated plan doc (docs/plans/current_spec.md previously held the "What's New Changelog" plan) so it isn't lost when this fix's own plan replaced it.

Test plan

  • go build ./...
  • go test ./... (full suite)
  • make lint-fast (staticcheck etc.) — 0 issues
  • scripts/go-test-coverage.sh — 89.2% (gate 87%)
  • scripts/local-patch-report.sh — artifacts generated
  • lefthook run pre-commit — full suite passed
  • lefthook run codeql (Go + JS) — 0 blocking findings; all 4 go/path-injection results resolved (verified via SARIF/jq); only the pre-existing, tracked go/cookie-secure-not-set warning remains
  • Trivy container scan — 2 HIGH findings, both pre-existing and documented in .trivyignore; 0 unsuppressed
  • govulncheck ./... — 0 vulnerabilities in Charon's own code
  • GORM security scan — correctly skipped (no internal/models/** or GORM query changes)
  • auth_handler.go confirmed untouched throughout

Wikid82 added 5 commits August 3, 2026 17:30
Introduce a strings.HasPrefix-based path containment helper alongside
its dedicated test suite. Not yet wired into any production call
site; a subsequent commit applies it at the filesystem sink guards in
system_permissions_handler.go.
Apply isWithinAllowlistBounds inline immediately before each of the
four filesystem sink calls in repairPath and pathHasSymlink, using the
exact value that reaches each sink. pathHasSymlink now takes the
normalized allowlist and returns a new sentinel error when a walked
path component falls outside it. No externally observable behavior
change: every existing repairPath/pathHasSymlink test passes with its
assertions unmodified.
…ation handler

Add direct, function-local containment guards immediately before each
filesystem sink in the permissions-repair path, plus new test coverage
for the allowlist-bounds decision logic, the symlink-walk rejection
path, and a symlink-escaping-allowlist regression case. Also corrects
a pre-existing test that exercised the wrong rejection branch instead
of the Lstat error path it was meant to cover.

No externally observable behavior change: request/response shapes,
status codes, and error codes are unchanged.
…ntation

isWithinAllowlistBounds and its dedicated test were never called from
production code -- the actual sink guards use firstAllowlistPrefix plus
an inline strings.HasPrefix check in each sink's own function, added in
the previous commit. Delete the dead helper and its test, add direct
test coverage for firstAllowlistPrefix (the function actually in use),
and update docs/plans/current_spec.md's as-built notes and acceptance
criteria to describe the mechanism that was actually shipped. Also adds
the previously-untracked issue doc for the separate, out-of-scope
auth_handler.go finding referenced throughout this fix's scope notes.
Preserves the prior docs/plans/current_spec.md content under
docs/plans/archive/ rather than leaving it as an untracked file, after
current_spec.md was rewritten for the path-injection fix work.
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.33333% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...nternal/api/handlers/system_permissions_handler.go 93.33% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-advanced-security

Copy link
Copy Markdown
Contributor

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

✅ Supply Chain Verification Results

PASSED

📦 SBOM Summary

  • Components: 1495

🔍 Vulnerability Scan

Severity Count
🔴 Critical 0
🟠 High 0
🟡 Medium 5
🟢 Low 2
Total 14

📎 Artifacts

  • SBOM (CycloneDX JSON) and Grype results available in workflow artifacts

Generated by Supply Chain Verification workflow • View Details

Wikid82 added 2 commits August 4, 2026 01:14
Codecov's patch status was configured with informational: true and
documented across testing.instructions.md and copilot-instructions.md
as "a suggestion" that "will not block PR approval." In practice it
does block merges, so the docs were wrong, not the enforcement.
Correct all three: codecov.yml now enforces patch coverage (target
aligned with the project coverage target / CHARON_MIN_COVERAGE
default of 87%, rather than the previously inconsistent 90%), and
both instruction files now describe it as a mandatory Definition of
Done gate.
Extract the outside-allowlist permissionsRepairResult literal (already
duplicated at the pre-existing isWithinAllowlist guard and the new
strings.HasPrefix guard) into a single outsideAllowlistResult helper.
No behavior change -- both call sites return the exact same status,
error code, and message as before.

This also closes the patch-coverage gap on the new guard's
structurally-unreachable rejection branch: it previously duplicated
the full 6-line result literal (never executed in practice, since
firstAllowlistPrefix is a proven superset of isWithinAllowlist's
containment decision for any input that reaches it), inflating the
uncovered footprint. Sharing the constructor shrinks that branch to a
single already-covered-elsewhere call, without writing any test that
doesn't reflect a real reachable state.
@Wikid82
Wikid82 merged commit 379a640 into development Aug 4, 2026
44 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants