Skip to content

Harden backup runner, env-file reads, and notification clients#175

Open
jonhadfield wants to merge 1 commit into
mainfrom
review-fixes
Open

Harden backup runner, env-file reads, and notification clients#175
jonhadfield wants to merge 1 commit into
mainfrom
review-fixes

Conversation

@jonhadfield

Copy link
Copy Markdown
Owner

Summary

Security and robustness fixes from a code review, across the backup runner, env-file indirection, and notification clients. Builds clean, passes go vet, and unit tests pass (live provider integration tests still gated on credentials).

Changes

  • backup: cleanupWorkingDir refuses to RemoveAll any path that doesn't resolve inside the backup directory — guards against a misconfigured GIT_WORKING_DIR wiping arbitrary filesystem locations.
  • backup: graceful SIGINT/SIGTERM shutdown via waitForShutdown, so an in-flight job (and its deferred cleanup) finishes before the process exits.
  • backup: replaced logger.Fatal* calls in library code with returned errors; split execProviderBackups into a testable runProviderBackups; converted the checkProviderFactory closure into checkProvider returning (count, error) instead of calling Fatalln from a library function.
  • backup: log the resolved git version at startup (non-fatal probe).
  • envfile: cap *_FILE reads at 1 MiB (maxEnvFileSize) so a device-backed path like /dev/zero cannot OOM the process.
  • notify: stop logging the token-bearing Telegram URL and stop echoing response bodies; return on ntfy request error.
  • webhook: build a dedicated retryablehttp client instead of mutating the shared one used by provider calls.
  • constants: tighten working dir mode 0o7550o700; drop the numUserDefinedProviders package global.

Test plan

  • go build ./... — clean
  • go vet ./... — clean
  • go test ./... — passes with provider credentials unset (live integration tests skip)

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings June 23, 2026 16:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens soba’s backup execution and notification pathways by reducing risky filesystem operations, improving shutdown behavior for scheduled runs, tightening secret/env-file handling, and avoiding credential leakage through HTTP client logging.

Changes:

  • Hardened backup execution: safer working-dir cleanup, graceful SIGINT/SIGTERM shutdown, refactored provider validation to return errors instead of exiting.
  • Hardened configuration input: capped *_FILE reads to 1 MiB and tightened working-dir permissions to 0o700.
  • Hardened outbound notifications: reduced Telegram token exposure, stopped logging response bodies, improved ntfy error handling, and isolated webhook retry configuration from shared clients.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
internal/webhook.go Uses a dedicated retryablehttp client for webhook retries to avoid mutating the shared provider client.
internal/notify.go Prevents Telegram token leakage via retryablehttp logging; avoids logging response bodies; returns on ntfy request error.
internal/envfile.go Adds a size cap when reading *_FILE env indirection to prevent OOM/device-backed reads.
internal/constants.go Tightens working-dir permissions and introduces maxEnvFileSize.
internal/backup.go Adds safer working-dir cleanup, graceful scheduler shutdown, non-fatal git version probe, and provider-validation refactor.
internal/backup_test.go Removes now-obsolete global reset.
go.mod Bumps dependencies and updates the declared Go version.
go.sum Updates dependency checksums accordingly.
Comments suppressed due to low confidence (1)

go.mod:3

  • The go directive should not include a patch version (e.g. 1.25.10). The go line is the language version and is typically go 1.25 / go 1.25.0; if you need to pin a specific toolchain patch, use the separate toolchain go1.25.10 directive.
go 1.25.1

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

Comment thread internal/backup.go
Comment on lines +324 to +335
if slices.Contains(justTokenProviders, provider) {
for _, param := range enabledProviderAuth[provider] {
val, exists := GetEnvOrFile(param)
if exists {
if strings.Trim(val, " ") == "" {
_, _ = fmt.Fprintf(&outputErrs, "%s parameter '%s' is not defined.\n", provider, param)
} else {
count++
}
}
}
}
Comment thread internal/notify.go
defer resp.Body.Close()

buf, err := io.ReadAll(resp.Body)
_, err = io.ReadAll(resp.Body)
Comment thread internal/notify.go
Comment on lines +149 to +151
// Do not log response body — Telegram error responses can echo
// chat_id and other metadata that is sensitive in shared deployments.
logger.Printf("telegram failed to send message - code [%d]", resp.StatusCode)
Comment thread internal/envfile.go
Comment on lines +45 to 49
b, err := io.ReadAll(io.LimitReader(f, maxEnvFileSize+1))
if err != nil {
logger.Printf("error reading file %s: %v", filePath, err)

return "", false
Comment thread internal/backup.go
Comment on lines +136 to +140
// cleanupWorkingDir removes the working directory after a backup run.
// It refuses to remove anything that does not resolve to a path inside
// backupDir to protect against a misconfigured GIT_WORKING_DIR wiping
// arbitrary filesystem locations.
func cleanupWorkingDir(workingDir, backupDir string) {
Security and robustness fixes from code review:

- backup: refuse to remove a working dir outside the backup dir, guarding
  against a misconfigured GIT_WORKING_DIR wiping arbitrary paths
- backup: graceful SIGINT/SIGTERM shutdown so in-flight jobs (and their
  deferred cleanup) complete before exit
- backup: replace logger.Fatal calls in library code with returned errors;
  split execProviderBackups into a testable runProviderBackups and convert
  the provider-check closure into checkProvider returning (count, error)
- backup: log resolved git version at startup
- envfile: cap *_FILE reads at 1 MiB so a device-backed path cannot OOM
- notify: stop logging the token-bearing Telegram URL and response bodies;
  return on ntfy request error
- webhook: use a dedicated retryablehttp client instead of mutating the
  shared one
- constants: tighten working dir mode 0o755 -> 0o700; drop the
  numUserDefinedProviders global

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

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