Skip to content

fix(core): harden config, path, and output trust boundaries#976

Open
ben-ranford wants to merge 1 commit into
mainfrom
bug/issues-839-865-866-867-868-869-870-security-hardening
Open

fix(core): harden config, path, and output trust boundaries#976
ben-ranford wants to merge 1 commit into
mainfrom
bug/issues-839-865-866-867-868-869-870-security-hardening

Conversation

@ben-ranford
Copy link
Copy Markdown
Owner

Issue

Cause

  • Explicit --config handling could become a trust signal for remote policy packs.
  • Runtime hook discovery walked every ancestor of the executable path.
  • Git quoted paths, webhook redaction, Slack fallback text, table output, and cache symlink handling all missed trust-boundary or control-character normalization.

Root cause

  • The implementation inferred trust from convenience paths and emitted repo-controlled strings without consistently decoding or sanitizing them at the output boundary.

Fix

  • Remove remote policy-pack trust escalation from explicit config handling and reject remote packs without fetching.
  • Anchor runtime hook lookup to fixed install-layout roots instead of ancestor walking.
  • Decode quoted git paths, escape Slack fallback text, expand webhook redaction candidates, sanitize report table output, and reject broken symlink cache escapes.

Tests

  • go test ./internal/gitexec ./internal/thresholds ./internal/runtime ./internal/workspace ./internal/notify ./internal/report ./internal/analysis ./internal/safeio ./internal/app
  • Additional focused regression tests were added in the touched package suites.

Closes #839
Closes #865
Closes #866
Closes #867
Closes #868
Closes #869
Closes #870

Copilot AI review requested due to automatic review settings June 2, 2026 13:16
@ben-ranford ben-ranford added this to the v1.6.0 milestone Jun 2, 2026
@ben-ranford ben-ranford added the bug Something isn't working label Jun 2, 2026
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented Jun 2, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
12.1% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Jun 2, 2026

Memory Benchmarks

❌ Memory benchmark summary was not produced.

Approval: unavailable because the memory benchmark gate did not complete cleanly.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Security-hardening sweep that tightens multiple trust boundaries across config/policy-pack loading, runtime hook discovery, git path parsing, notification redaction/escaping, CLI table output sanitization, and cache path safety.

Changes:

  • Disable remote policy packs (no fetching) and adjust policy-pack trust handling/error messaging + tests.
  • Anchor runtime hook lookup to fixed, non-ancestor-walk roots and add targeted tests.
  • Normalize/sanitize repo-controlled strings at output boundaries (git quoted paths decoding, Slack fallback escaping, webhook redaction expansion, table-output terminal control scrubbing, cache symlink escape rejection).

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
internal/workspace/workspace_changed_files_test.go Adds regression coverage for decoding git-quoted non-ASCII paths from diff/status.
internal/workspace/changed_files.go Decodes git C-quoted paths (defense-in-depth) in diff/status parsing.
internal/thresholds/config.go Updates policy resolver trust initialization signature for hardened pack resolution.
internal/thresholds/config_test.go Updates/reshapes tests to assert remote policy packs are rejected without fetching.
internal/thresholds/config_packs.go Removes “explicit config implies remote trust” and hard-disables remote packs; adjusts local-root trust.
internal/thresholds/config_cov_more_http_test.go Updates coverage test setup after packTrust changes.
internal/runtime/capture_env.go Reworks runtime hook search roots to anchored locations; adds injectable providers for tests.
internal/runtime/capture_env_test.go Adds anchored-root test and utilities for overriding executable/caller providers.
internal/report/terminal.go Introduces terminal control-character sanitizer for report/table output.
internal/report/format_test.go Adds regression test ensuring table output escapes control characters.
internal/report/format_table.go Sanitizes string args emitted through table formatter helpers; sanitizes key row fields.
internal/report/format_table_values.go Sanitizes table value renderers that may include repo-controlled strings.
internal/report/format_table_sections.go Sanitizes additional table sections (scope, policy sources, comparison keys, codemod, warnings).
internal/notify/slack.go Escapes Slack top-level fallback text to prevent mrkdwn injection from repo names.
internal/notify/slack_test.go Adds tests covering fallback escaping for common mrkdwn/HTML-sensitive characters.
internal/notify/dispatcher.go Expands webhook URL redaction candidates to cover more real-world error-message variants.
internal/notify/dispatcher_test.go Adds tests for redacting path-escaped fragments, bare host+path, and bare token leakage.
internal/gitexec/gitexec.go Forces core.quotePath=false to reduce quoted-path surface area in git outputs.
internal/analysis/cache.go Treats symlinked cache roots as escape attempts (including broken symlinks) via Lstat precheck.
internal/analysis/cache_extra_test.go Adds regression test ensuring broken symlink cache paths are rejected and don’t create dirs outside repo.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +140 to 142
if _, filename, _, ok := runtimeCaller(0); ok {
addSearchPath(filepath.Clean(filepath.Join(filepath.Dir(filename), "..", "..")))
}
Comment on lines +125 to +129
roots := runtimeHookSearchRoots()
want := []string{
filepath.Clean(filepath.Join("/tmp", "plant", "bin", "..", "share", "lopper")),
filepath.Clean(filepath.Join("/tmp", "source", "internal", "runtime", "..", "..")),
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment