Skip to content

πŸ›‘οΈ Sentinel: [HIGH] Fix predictable temp files in atomic writes#75

Closed
himattm wants to merge 1 commit intomainfrom
sentinel/atomic-write-symlink-fix-17412171799410978877
Closed

πŸ›‘οΈ Sentinel: [HIGH] Fix predictable temp files in atomic writes#75
himattm wants to merge 1 commit intomainfrom
sentinel/atomic-write-symlink-fix-17412171799410978877

Conversation

@himattm
Copy link
Copy Markdown
Owner

@himattm himattm commented Apr 29, 2026

🚨 Severity: HIGH
πŸ’‘ Vulnerability: Predictable .tmp file paths were used with os.WriteFile during atomic writes, making the application susceptible to symlink/TOCTOU attacks.
🎯 Impact: If exploited, attackers could use symlinks for predictable filenames to overwrite sensitive or arbitrary files, depending on permissions and application context.
πŸ”§ Fix: Replaced the predictable .tmp paths with os.CreateTemp(filepath.Dir(path), filepath.Base(path)+"-*") to ensure unpredictability and proper directory isolation. Added explicit Chmod and Close calls to maintain correct permissions and lock safety before the atomic os.Rename.
βœ… Verification: Ran go test ./... and verified that no existing tests failed and formats complied.


PR created automatically by Jules for task 17412171799410978877 started by @himattm

This commit secures the atomic file saving operations in `internal/burnrate/burnrate.go` and `internal/sparkline/sparkline.go`.

**Vulnerability:** Previously, `os.WriteFile` was used with predictable temporary file paths (e.g., `path + ".tmp"`). This exposed the application to symlink attacks, allowing unauthorized modifications by replacing the predicted `.tmp` path with a symbolic link before the write occurs.

**Fix:** Refactored the file save operations to use `os.CreateTemp` from the `os` package, securely generating unpredictable filenames in the same directory as the target path. The temporary file is explicitly chmoded (0644) and closed before being atomically renamed to its final location.

**Verification:** Validated correct behavior by running `go test ./...` and `go fmt ./...`. The updated implementation ensures temporary files are safely created and handles cross-device rename scenarios gracefully by generating the temp file in `filepath.Dir(path)`.

Co-authored-by: himattm <6266621+himattm@users.noreply.github.com>
@google-labs-jules
Copy link
Copy Markdown
Contributor

πŸ‘‹ Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a πŸ‘€ emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a security vulnerability by replacing predictable temporary file paths with unpredictable ones using os.CreateTemp for atomic file writes. The changes span documentation and implementation in the burnrate and sparkline packages. Feedback includes a suggestion to use a defer block for more idiomatic and robust cleanup of temporary files, and a recommendation to handle the ignored error from os.Rename to avoid leaving orphaned files on disk.

Comment on lines +73 to 92
if _, err := tmpFile.Write(data); err != nil {
tmpFile.Close()
os.Remove(tmpPath)
return nil, false, err
}

if err := tmpFile.Chmod(0644); err != nil {
tmpFile.Close()
os.Remove(tmpPath)
return nil, false, err
}

if err := tmpFile.Close(); err != nil {
os.Remove(tmpPath)
return nil, false, err
}

if err := os.Rename(tmpPath, path); err != nil {
os.Remove(tmpPath)
return nil, false, err
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The error handling for the temporary file operations is quite repetitive and prone to errors if new steps are added. Using a defer block with a cleanup flag is a more idiomatic Go pattern that ensures resources are released and temporary files are cleaned up on any error path.

	cleanup := true
	defer func() {
		tmpFile.Close()
		if cleanup {
			os.Remove(tmpPath)
		}
	}()

	if _, err := tmpFile.Write(data); err != nil {
		return nil, false, err
	}

	if err := tmpFile.Chmod(0644); err != nil {
		return nil, false, err
	}

	if err := tmpFile.Close(); err != nil {
		return nil, false, err
	}

	if err := os.Rename(tmpPath, path); err != nil {
		return nil, false, err
	}
	cleanup = false

}
os.Rename(tmp, path)

os.Rename(tmpPath, path)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The os.Rename call ignores the return error. If the rename fails (e.g., due to permission issues or filesystem limits), the temporary file will be left orphaned on disk. While the filenames are now unpredictable, it is still best practice to clean up the temporary file if the atomic swap fails.

	if err := os.Rename(tmpPath, path); err != nil {
		os.Remove(tmpPath)
	}

@himattm
Copy link
Copy Markdown
Owner Author

himattm commented May 7, 2026

Closing as superseded by direct commit 42f3149 on main, which consolidated the burnrate/sparkline predictable-temp-file fixes and also preserved PR #82's updater temp-file hardening without the generated .jules note.

@himattm himattm closed this May 7, 2026
@himattm himattm deleted the sentinel/atomic-write-symlink-fix-17412171799410978877 branch May 7, 2026 15:35
@google-labs-jules
Copy link
Copy Markdown
Contributor

Closing as superseded by direct commit 42f3149 on main, which consolidated the burnrate/sparkline predictable-temp-file fixes and also preserved PR #82's updater temp-file hardening without the generated .jules note.

Understood. Acknowledging that this work is now superseded and stopping work on this task.

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