Skip to content

fix(security): prevent arbitrary file write in machine config (P1+P2)#102

Merged
nvandessel merged 2 commits intomainfrom
fix/sl1-kss-machine-config-write
Feb 27, 2026
Merged

fix(security): prevent arbitrary file write in machine config (P1+P2)#102
nvandessel merged 2 commits intomainfrom
fix/sl1-kss-machine-config-write

Conversation

@nvandessel
Copy link
Owner

@nvandessel nvandessel commented Feb 8, 2026

Summary

  • Require ~/ prefix for machine config destinations
  • Validate expanded paths stay within home directory via validation.ValidateDestinationPath()
  • Change file permissions from 0644 to 0600 (not world-readable)
  • Change directory permissions from 0755 to 0700

Beads

  • go4dot-sl1 (machine config arbitrary write)
  • go4dot-kss (file permissions - partial)

Test plan

  • Paths not starting with ~/ rejected
  • Path traversal (~/../../etc) rejected
  • Files created with 0600 permissions
  • Directories created with 0700 permissions
  • make build && make lint && make test passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Enforced stricter validation for destination paths requiring tilde-prefixed home paths and preventing traversal.
    • Tightened file system permissions for created directories and files (700 for dirs, 600 for files).
  • Tests

    • Expanded coverage for tilde-based paths, traversal rejection, overwrite/dry-run behaviors, and permission verification.

@coderabbitai
Copy link

coderabbitai bot commented Feb 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c399d1f and a2e10ad.

📒 Files selected for processing (2)
  • internal/machine/templates.go
  • internal/machine/templates_test.go

📝 Walkthrough

Walkthrough

Enforces tilde-prefixed destinations that expand within the user's home directory and adds validation for expanded paths. Tightens file and directory creation permissions to 0600 (files) and 0700 (directories). Tests updated to use home-backed temp dirs, tilde helpers, and permission assertions.

Changes

Cohort / File(s) Summary
Path Validation & Permissions
internal/machine/templates.go
Require destinations start with ~/, validate expanded path stays under $HOME, return errors on invalid expansions, and tighten created directory/file modes to 0700/0600.
Testing & Helpers
internal/machine/templates_test.go
Add homeTempDir() and tildeRelPath() helpers; convert tests to use tilde-relative destinations; add tests for expandPath (tilde acceptance, traversal rejection) and permission checks; assert 0700 dir and 0600 file modes.
Test imports
internal/machine/... tests
Import io/fs for permission checks and adjust test file creation modes to match stricter permissions.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through paths with careful paws,
I guard the tilde and check the laws,
0700 burrows, 0600 locks,
Tests nibble crumbs and pass the knocks,
A happy rabbit guards your box. 🥕🔐

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main security fix: preventing arbitrary file writes in machine config by enforcing path constraints and validating destination paths.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/sl1-kss-machine-config-write

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Feb 8, 2026

Codecov Report

❌ Patch coverage is 90.90909% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 48.00%. Comparing base (bbc3182) to head (c399d1f).

Files with missing lines Patch % Lines
internal/machine/templates.go 90.90% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #102      +/-   ##
==========================================
+ Coverage   47.99%   48.00%   +0.01%     
==========================================
  Files         107      107              
  Lines       11718    11721       +3     
==========================================
+ Hits         5624     5627       +3     
  Misses       6094     6094              
Files with missing lines Coverage Δ
internal/machine/templates.go 82.24% <90.90%> (+0.51%) ⬆️

... and 1 file with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@greptile-apps
Copy link

greptile-apps bot commented Feb 8, 2026

Greptile Overview

Greptile Summary

This PR addresses critical security vulnerabilities (P1+P2 priority) by preventing arbitrary file write attacks in machine config functionality.

Key changes:

  • Created new internal/validation package with comprehensive input validators for path traversal, command injection, and flag injection prevention
  • Modified expandPath() in internal/machine/templates.go to require ~/ prefix and validate expanded paths stay within home directory using validation.ValidateDestinationPath()
  • Hardened file permissions from world-readable 0644/0755 to owner-only 0600/0700
  • Added extensive test coverage (321+ test cases) covering security edge cases and attack patterns

The implementation uses a defense-in-depth approach:

  1. Path prefix validation (must start with ~/)
  2. Path traversal validation using filepath.Rel() to detect .. escapes
  3. Restrictive file permissions preventing unauthorized access

Test plan coverage is comprehensive and all security test cases pass.

Confidence Score: 5/5

  • This PR is safe to merge with high confidence - it addresses critical security vulnerabilities with proper validation and comprehensive test coverage
  • Score reflects thorough security hardening with defense-in-depth approach: (1) new validation package with comprehensive input validators, (2) path traversal prevention using multiple validation layers, (3) hardened file permissions to owner-only, (4) extensive test coverage with 321+ test cases covering attack patterns, (5) clean implementation following Go best practices
  • No files require special attention - all changes are well-tested security improvements

Important Files Changed

Filename Overview
internal/validation/validation.go New validation package with comprehensive security validators for path traversal, command injection, and flag injection prevention
internal/machine/templates.go Updated expandPath to require ~/ prefix, validate paths with validation.ValidateDestinationPath(), and changed permissions to 0600/0700

Sequence Diagram

sequenceDiagram
    participant User
    participant RenderAndWrite
    participant expandPath
    participant ValidateDestinationPath
    participant FileSystem

    User->>RenderAndWrite: Write machine config<br/>(destination: "~/../../etc/shadow")
    RenderAndWrite->>expandPath: Expand path
    
    alt Path doesn't start with ~/
        expandPath-->>RenderAndWrite: Error: must start with ~/
        RenderAndWrite-->>User: Error returned
    else Path starts with ~/
        expandPath->>expandPath: Get home directory
        expandPath->>expandPath: Join home + path[2:]<br/>Clean result
        expandPath->>ValidateDestinationPath: Validate(expanded, home)
        
        alt Path escapes home directory
            ValidateDestinationPath->>ValidateDestinationPath: filepath.Rel(home, expanded)
            ValidateDestinationPath->>ValidateDestinationPath: Check if starts with ".."
            ValidateDestinationPath-->>expandPath: Error: escapes base directory
            expandPath-->>RenderAndWrite: Error: invalid destination
            RenderAndWrite-->>User: Error returned
        else Path stays within home
            ValidateDestinationPath-->>expandPath: OK
            expandPath-->>RenderAndWrite: Valid expanded path
            RenderAndWrite->>FileSystem: MkdirAll(parentDir, 0700)
            RenderAndWrite->>FileSystem: WriteFile(path, content, 0600)
            FileSystem-->>RenderAndWrite: Success
            RenderAndWrite-->>User: File created securely
        end
    end
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile


expanded := filepath.Clean(filepath.Join(home, path[2:]))

if err := validation.ValidateDestinationPath(expanded, home); err != nil {
Copy link

Choose a reason for hiding this comment

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

Consider if internal/deps/external.go:304 should use similar validation logic, as it also has an expandPath function that handles ~/ paths but doesn't validate against path traversal.

@nvandessel nvandessel force-pushed the fix/sl1-kss-machine-config-write branch from c202581 to 46095fe Compare February 9, 2026 16:54
@nvandessel
Copy link
Owner Author

@greptile review

@greptile-apps
Copy link

greptile-apps bot commented Feb 10, 2026

Greptile Overview

Greptile Summary

This change hardens machine config rendering/writing by requiring destinations to be specified as ~/..., expanding them to the user’s home directory, and rejecting any expanded path that escapes $HOME via validation.ValidateDestinationPath(). It also tightens filesystem permissions for generated configs (directories 0700, files 0600) and updates the unit tests accordingly, including new coverage for traversal rejection and permission modes.

The main issue to address before merge is in the tests: several new assertions depend on exact error-message substrings (e.g., "escapes base directory"), making the suite unnecessarily brittle to harmless error wording changes.

Confidence Score: 4/5

  • This PR is mostly safe to merge once the brittle test assertions are addressed.
  • Core security logic (requiring ~/ and validating expanded paths stay under $HOME) is straightforward and consistently applied in render/write/status/remove flows. The only clear issue found is test fragility from asserting on specific error message text, which can cause spurious CI failures without indicating a behavior regression.
  • internal/machine/templates_test.go

Important Files Changed

Filename Overview
internal/machine/templates.go Tightens destination handling by requiring "~/" and validating expanded paths remain under $HOME; also changes mkdir/file modes to 0700/0600.
internal/machine/templates_test.go Updates tests to use ~/ destinations under $HOME and adds new permission/traversal tests; introduces brittle assertions on specific error message substrings from ValidateDestinationPath.

Sequence Diagram

sequenceDiagram
  participant Caller as CLI/Caller
  participant Machine as internal/machine/templates.go
  participant OS as os/filesystem
  participant Val as internal/validation

  Caller->>Machine: RenderAndWrite(mc, values, opts)
  Machine->>Machine: RenderMachineConfig(mc, values)
  Machine->>Machine: expandPath(mc.Destination)
  Machine->>OS: UserHomeDir()
  Machine->>Machine: Clean(Join(home, path[2:]))
  Machine->>Val: ValidateDestinationPath(expanded, home)
  alt invalid path (no "~/" or traversal)
    Val-->>Machine: error
    Machine-->>Caller: error ("failed to expand..." / "invalid destination...")
  else valid path
    Val-->>Machine: nil
    Machine->>OS: MkdirAll(parentDir, 0700)
    Machine->>OS: WriteFile(dest, content, 0600)
    Machine-->>Caller: RenderResult
  end
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

Comment on lines 481 to 501
func TestExpandPathRejectsTraversal(t *testing.T) {
tests := []struct {
name string
input string
}{
{name: "parent traversal", input: "~/../../etc/shadow"},
{name: "deep traversal", input: "~/../../../tmp/evil"},
{name: "single parent", input: "~/.."},
{name: "dotdot in middle", input: "~/.config/../../etc/passwd"},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := expandPath(tt.input)
if err == nil {
t.Errorf("expandPath(%q) should have returned error for path traversal", tt.input)
}
if err != nil && !strings.Contains(err.Error(), "escapes base directory") {
t.Errorf("expandPath(%q) error = %q, expected 'escapes base directory' message", tt.input, err.Error())
}
})
Copy link

Choose a reason for hiding this comment

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

Brittle error string assertions

TestExpandPathRejectsTraversal asserts the error contains the exact substring "escapes base directory" (and TestExpandPathRejectsNonTildePaths asserts "must start with ~/"). This will fail as soon as validation.ValidateDestinationPath() changes its message wording, or if expandPath() wraps/augments the error differently. These tests should assert on behavior (i.e., that an error is returned) rather than a particular message text, or at least match on a stable sentinel (e.g., a typed error) if available.

Also appears at internal/machine/templates_test.go:469-476.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

nvandessel and others added 2 commits February 27, 2026 15:22
Require ~/ prefix for machine config destinations.
Validate expanded paths stay within home directory.
Change file permissions from 0644 to 0600 (not world-readable).
Change directory permissions from 0755 to 0700.

Beads: go4dot-sl1, go4dot-kss

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Test behavior (error returned) rather than coupling to specific error
message text. This prevents test breakage when error messages are
reworded in validation functions.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@nvandessel nvandessel force-pushed the fix/sl1-kss-machine-config-write branch from c399d1f to a2e10ad Compare February 27, 2026 23:23
@nvandessel nvandessel merged commit edf18ce into main Feb 27, 2026
3 of 5 checks passed
@nvandessel nvandessel deleted the fix/sl1-kss-machine-config-write branch February 27, 2026 23:23
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.

1 participant