Skip to content

Conversation

@kidchenko
Copy link
Owner

@kidchenko kidchenko commented Feb 1, 2026

🛡️ Sentinel: [CRITICAL] Fix SSH private key creation race condition

🚨 Severity: CRITICAL
💡 Vulnerability: TOCTOU race condition in tools/setup-ssh-keys.sh allowed private keys to be world-readable between creation and permission hardening.
🎯 Impact: On multi-user systems or if a malicious process is running, the private key could be read during the brief window before chmod 600 was executed.
🔧 Fix: Used umask 077 to ensure the file is created with secure permissions (0600) from the start.
✅ Verification: Added tests/verify_ssh_permissions.sh which mocks op and verifies that the restored keys have the correct permissions. Confirmed manually that umask affects the file creation.


PR created automatically by Jules for task 6068129369537875415 started by @kidchenko

Summary by CodeRabbit

  • Bug Fixes

    • Fixed a critical security vulnerability in SSH key setup where private keys were briefly world-readable before proper permission restrictions were applied, potentially exposing sensitive credentials.
  • Tests

    • Added verification tests to ensure SSH key files are consistently created with correct and secure permissions.

✏️ Tip: You can customize this high-level summary in your review settings.

Identified and fixed a race condition in `tools/setup-ssh-keys.sh` where the private key file was created with default permissions (potentially world-readable) before being restricted with `chmod 600`.

Changes:
- Wrapped sensitive file creation in `umask 077` block to ensure files are created with 600 permissions immediately.
- Added `tests/verify_ssh_permissions.sh` to verify that the script restores keys with correct permissions.
- Updated `.jules/sentinel.md` with the security learning.

This eliminates the window of exposure for private keys during restoration.

Co-authored-by: kidchenko <5432753+kidchenko@users.noreply.github.com>
@google-labs-jules
Copy link
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.

@coderabbitai
Copy link

coderabbitai bot commented Feb 1, 2026

📝 Walkthrough

Walkthrough

This change addresses a TOCTOU vulnerability in SSH private key creation where keys were temporarily world-readable due to shell umask inheritance. The fix implements safe umask handling in setup-ssh-keys.sh, adds a test to verify correct permissions, and documents the vulnerability discovered.

Changes

Cohort / File(s) Summary
Vulnerability Documentation
.jules/sentinel.md
Added diary entry documenting SSH private key TOCTOU race condition where output redirection inherits shell umask (commonly 022), briefly exposing private keys. Notes that relying on later chmod is insufficient; recommends setting restrictive umask (077) before sensitive file creation.
Permission Verification Test
tests/verify_ssh_permissions.sh
New test script that validates SSH key restoration creates files with correct permissions (private keys 600, public keys 644, directory 700) by mocking 1Password utilities and verifying the setup-ssh-keys.sh output.
SSH Key Restoration Fix
tools/setup-ssh-keys.sh
Updated cmd_restore to safely create SSH directory with 700 permissions and use temporary umask 077 during private key creation, followed by explicit chmod 600 and umask restoration. Eliminates the window where private keys are world-readable.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A rabbit secured the keys with care,
No umask'd files left bare in the air,
With mode 077, the safety's ensured,
Permissions restrictive, our secrets are lured, 🔐
No TOCTOU race can catch us unaware!

🚥 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 accurately describes the main security fix: addressing an SSH private key TOCTOU race condition. While it includes an emoji and severity tag, these don't obscure the core change.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch sentinel/fix-ssh-key-race-condition-6068129369537875415

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.jules/sentinel.md:
- Around line 1-4: Update .jules/sentinel.md to satisfy markdownlint: make the
top heading an H1 (prepend "# " to the first line) and ensure there's a blank
line after it, wrap the following lines (the SSH Private Key Race Condition
paragraph, Vulnerability, Learning, and Prevention lines) to <=80 characters per
line, and change "afterwards" to US spelling "afterward"; keep the same content
and references to tools/setup-ssh-keys.sh, umask, and chmod 600 so the context
remains clear.
🧹 Nitpick comments (2)
tools/setup-ssh-keys.sh (1)

159-171: Restore umask and clean partial key on op read failure.

op read ... > "$PRIVATE_KEY_FILE" creates/truncates the file before the command runs. If op read fails, an empty private key can be left behind and the umask may stay changed until exit. Consider explicit error handling and cleanup.

♻️ Suggested fix
     local old_umask
     old_umask=$(umask)
     umask 077

     # Read private key from 1Password and save locally
     # umask 077 ensures file is created with 600 permissions
-    op read "op://$VAULT/$KEY_NAME/private_key" > "$PRIVATE_KEY_FILE"
+    if ! op read "op://$VAULT/$KEY_NAME/private_key" > "$PRIVATE_KEY_FILE"; then
+        umask "$old_umask"
+        rm -f "$PRIVATE_KEY_FILE"
+        error "Failed to read private key from 1Password"
+    fi
     # Redundant chmod just to be sure, but file was created securely
     chmod 600 "$PRIVATE_KEY_FILE"

     # Restore umask for public key (we want 644)
     umask "$old_umask"
tests/verify_ssh_permissions.sh (1)

10-15: Ensure temp artifacts are cleaned up on failures.

With set -e, any failure exits before the final cleanup, leaving temp dirs behind. Add a trap so cleanup always runs.

🧹 Suggested fix
 # Clean up previous run
 rm -rf "$TEMP_HOME"
 rm -rf "$MOCK_BIN"
 mkdir -p "$TEMP_HOME"
 mkdir -p "$MOCK_BIN"
+
+cleanup() {
+    rm -rf "$TEMP_HOME"
+    rm -rf "$MOCK_BIN"
+}
+trap cleanup EXIT
@@
-# Cleanup
-rm -rf "$TEMP_HOME"
-rm -rf "$MOCK_BIN"

Also applies to: 80-82

Comment on lines +1 to +4
## 2024-05-22 - [SSH Private Key Race Condition]
**Vulnerability:** Found a TOCTOU (Time-of-Check to Time-of-Use) race condition in `tools/setup-ssh-keys.sh`. The script created the private key file using output redirection (`>`) which uses the default umask (typically 022), resulting in the file being world-readable for a brief window before `chmod 600` was executed.
**Learning:** Shell redirection and standard file creation tools respect the current `umask`. Relying on a subsequent `chmod` to secure sensitive files leaves a window of exposure.
**Prevention:** Always set `umask 077` (or similar restrictive mask) *before* creating sensitive files in shell scripts to ensure they are born secure. Restore the original umask afterwards if necessary.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix markdownlint failures (MD041/MD022/MD013) and US spelling.

Line 1 must be an H1 with blank lines, and Lines 2–4 exceed the 80‑char limit; the linter will block the PR. Also prefer “afterward” for US English.

✅ Suggested fix
-## 2024-05-22 - [SSH Private Key Race Condition]
-**Vulnerability:** Found a TOCTOU (Time-of-Check to Time-of-Use) race condition in `tools/setup-ssh-keys.sh`. The script created the private key file using output redirection (`>`) which uses the default umask (typically 022), resulting in the file being world-readable for a brief window before `chmod 600` was executed.
-**Learning:** Shell redirection and standard file creation tools respect the current `umask`. Relying on a subsequent `chmod` to secure sensitive files leaves a window of exposure.
-**Prevention:** Always set `umask 077` (or similar restrictive mask) *before* creating sensitive files in shell scripts to ensure they are born secure. Restore the original umask afterwards if necessary.
+# 2024-05-22 - [SSH Private Key Race Condition]
+
+**Vulnerability:** Found a TOCTOU (Time-of-Check to Time-of-Use) race condition in
+`tools/setup-ssh-keys.sh`. The script created the private key file using output
+redirection (`>`), which uses the default umask (typically 022), resulting in the
+file being world-readable for a brief window before `chmod 600` was executed.
+
+**Learning:** Shell redirection and standard file creation tools respect the
+current `umask`. Relying on a subsequent `chmod` to secure sensitive files leaves
+a window of exposure.
+
+**Prevention:** Always set `umask 077` (or similar restrictive mask) *before*
+creating sensitive files in shell scripts to ensure they are born secure. Restore
+the original umask afterward if necessary.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## 2024-05-22 - [SSH Private Key Race Condition]
**Vulnerability:** Found a TOCTOU (Time-of-Check to Time-of-Use) race condition in `tools/setup-ssh-keys.sh`. The script created the private key file using output redirection (`>`) which uses the default umask (typically 022), resulting in the file being world-readable for a brief window before `chmod 600` was executed.
**Learning:** Shell redirection and standard file creation tools respect the current `umask`. Relying on a subsequent `chmod` to secure sensitive files leaves a window of exposure.
**Prevention:** Always set `umask 077` (or similar restrictive mask) *before* creating sensitive files in shell scripts to ensure they are born secure. Restore the original umask afterwards if necessary.
# 2024-05-22 - [SSH Private Key Race Condition]
**Vulnerability:** Found a TOCTOU (Time-of-Check to Time-of-Use) race condition in
`tools/setup-ssh-keys.sh`. The script created the private key file using output
redirection (`>`), which uses the default umask (typically 022), resulting in the
file being world-readable for a brief window before `chmod 600` was executed.
**Learning:** Shell redirection and standard file creation tools respect the
current `umask`. Relying on a subsequent `chmod` to secure sensitive files leaves
a window of exposure.
**Prevention:** Always set `umask 077` (or similar restrictive mask) *before*
creating sensitive files in shell scripts to ensure they are born secure. Restore
the original umask afterward if necessary.
🧰 Tools
🪛 GitHub Check: Lint Documentation

[failure] 4-4: Line length
.jules/sentinel.md:4:81 MD013/line-length Line length [Expected: 80; Actual: 203] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 3-3: Line length
.jules/sentinel.md:3:81 MD013/line-length Line length [Expected: 80; Actual: 180] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 2-2: Line length
.jules/sentinel.md:2:81 MD013/line-length Line length [Expected: 80; Actual: 321] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md013.md


[failure] 1-1: First line in a file should be a top-level heading
.jules/sentinel.md:1 MD041/first-line-heading/first-line-h1 First line in a file should be a top-level heading [Context: "## 2024-05-22 - [SSH Private K..."] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md041.md


[failure] 1-1: Headings should be surrounded by blank lines
.jules/sentinel.md:1 MD022/blanks-around-headings Headings should be surrounded by blank lines [Expected: 1; Actual: 0; Below] [Context: "## 2024-05-22 - [SSH Private Key Race Condition]"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md

🪛 LanguageTool

[locale-violation] ~4-~4: In American English, ‘afterward’ is the preferred variant. ‘Afterwards’ is more commonly used in British English and other dialects.
Context: ...born secure. Restore the original umask afterwards if necessary.

(AFTERWARDS_US)

🤖 Prompt for AI Agents
In @.jules/sentinel.md around lines 1 - 4, Update .jules/sentinel.md to satisfy
markdownlint: make the top heading an H1 (prepend "# " to the first line) and
ensure there's a blank line after it, wrap the following lines (the SSH Private
Key Race Condition paragraph, Vulnerability, Learning, and Prevention lines) to
<=80 characters per line, and change "afterwards" to US spelling "afterward";
keep the same content and references to tools/setup-ssh-keys.sh, umask, and
chmod 600 so the context remains clear.

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