Skip to content

Conversation

@kidchenko
Copy link
Owner

@kidchenko kidchenko commented Feb 10, 2026

This PR addresses a Time-of-Check to Time-of-Use (TOCTOU) vulnerability in tools/setup-ssh-keys.sh where SSH private keys were created with default umask permissions (often allowing group/world read) before being restricted with chmod 600.

The fix wraps the file creation commands in a subshell with umask 077, ensuring that files are born with secure permissions (600/700).

Also includes:

  • A new test script tests/test_ssh_creation.sh that mocks op CLI to verify the fix.
  • Updates to build.sh to include the tests/ directory in validation checks.
  • A Sentinel journal entry documenting the learning.

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

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced SSH key creation security to prevent private keys from being created with overly permissive file permissions, ensuring keys are secure from the moment of creation.
  • Tests

    • Added automated tests to verify SSH keys are created with correct file permissions (600 for private keys, 700 for SSH directory).

Fixed a race condition where SSH private keys were briefly world-readable during creation. The `op read ... > file` command created the file with default permissions (often 644) before `chmod 600` was applied.

Changes:
- Modified `tools/setup-ssh-keys.sh` to use `(umask 077; ...)` subshells for sensitive file creation.
- Added regression test `tests/test_ssh_creation.sh` to verify permissions.
- Updated `build.sh` to include `tests/` in syntax checks.
- Added security journal entry in `.jules/sentinel.md`.

Verification:
- Ran `tests/test_ssh_creation.sh` which confirmed correct permissions (600/700).
- Ran `./build.sh` to verify syntax and linting.

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 10, 2026

📝 Walkthrough

Walkthrough

This PR addresses an SSH key creation security vulnerability by implementing and testing a mitigation. Changes include documentation of the race condition, a test script validating SSH key permissions, modifications to tools/setup-ssh-keys.sh using restrictive umask, and build script updates to include test validation.

Changes

Cohort / File(s) Summary
Security Documentation
.jules/sentinel.md
New entry documenting SSH key creation race condition vulnerability and the preventive pattern using restricted umask to ensure secure file creation.
Test Infrastructure
tests/test_ssh_creation.sh, build.sh
New test script validating SSH key creation with correct permissions (600 for private key, 700 for directory); build script updated to include tests directory in shell script linting.
SSH Key Setup Implementation
tools/setup-ssh-keys.sh
Modified restoration flow to create SSH directory and read private keys within subshells with restrictive umask (077), preventing world-readable permissions during file creation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A rabbit's whisper on the wind so clear:
SSH keys now birth with umask dear,
No world-readable secrets in the night,
Tests ensure permissions are just right! ✨🔐

🚥 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 includes an emoji and bracketed severity marker that add noise, but accurately describes the main security fix—preventing SSH key creation with incorrect permissions via umask restriction.
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-creation-perms-271437817872940856

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: 2

🤖 Fix all issues with AI agents
In @.jules/sentinel.md:
- Around line 1-4: Update the entry header and fix markdownlint issues: change
the heading "## 2024-02-10 - [SSH Key Creation Race Condition]" to a top-level
heading and correct the date to "2026-02-10" (e.g. "# 2026-02-10 - [SSH Key
Creation Race Condition]"), ensure there is a blank line after that heading, and
reflow any lines longer than 80 characters (split the "Vulnerability",
"Learning", and "Prevention" lines into shorter sentences/paragraphs) so the
umask subshell recommendation "(umask 077; command > sensitive_file)" and other
text comply with MD013, MD022, and MD041.

In `@tests/test_ssh_creation.sh`:
- Around line 1-5: Add a cleanup function that removes TEST_DIR and register it
with trap so it runs on exit/failure (e.g., define cleanup() { rm -rf
"$TEST_DIR"; } and call trap cleanup EXIT or trap cleanup ERR EXIT) to ensure
temp dirs are removed when set -e causes early exit; reference the TEST_DIR
variable and remove the manual rm -rf "$TEST_DIR" at the end of the script so
the trap handles cleanup consistently.
🧹 Nitpick comments (1)
tests/test_ssh_creation.sh (1)

48-50: Test only covers the restore path — consider noting this limitation.

The test exercises restore but not generate, sync, or the interactive flow. This is fine for validating the TOCTOU fix, but worth noting if broader test coverage is planned.

Comment on lines +1 to +4
## 2024-02-10 - [SSH Key Creation Race Condition]
**Vulnerability:** SSH private keys were briefly world-readable during creation because `umask` was not restricted. The `op read ... > file` command created the file with default permissions (often 644/664), and `chmod 600` was only applied afterward.
**Learning:** Shell redirection creates files before `chmod` runs. This creates a Time-of-Check to Time-of-Use (TOCTOU) window where sensitive data is exposed.
**Prevention:** Always wrap sensitive file creation commands in a subshell with restricted umask: `(umask 077; command > sensitive_file)`. This ensures the file is born secure.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix markdown lint failures flagged by CI and correct the year.

  1. The date reads 2024-02-10 — should be 2026-02-10.
  2. CI reports several markdownlint violations: missing top-level heading (MD041), no blank line after heading (MD022), and lines exceeding 80 characters (MD013).
Proposed fix
-## 2024-02-10 - [SSH Key Creation Race Condition]
-**Vulnerability:** SSH private keys were briefly world-readable during creation because `umask` was not restricted. The `op read ... > file` command created the file with default permissions (often 644/664), and `chmod 600` was only applied afterward.
-**Learning:** Shell redirection creates files before `chmod` runs. This creates a Time-of-Check to Time-of-Use (TOCTOU) window where sensitive data is exposed.
-**Prevention:** Always wrap sensitive file creation commands in a subshell with restricted umask: `(umask 077; command > sensitive_file)`. This ensures the file is born secure.
+# Sentinel Journal
+
+## 2026-02-10 - SSH Key Creation Race Condition
+
+**Vulnerability:** SSH private keys were briefly world-readable during
+creation because `umask` was not restricted. The `op read ... > file`
+command created the file with default permissions (often 644/664), and
+`chmod 600` was only applied afterward.
+
+**Learning:** Shell redirection creates files before `chmod` runs. This
+creates a TOCTOU window where sensitive data is exposed.
+
+**Prevention:** Always wrap sensitive file creation in a subshell with
+restricted umask: `(umask 077; command > sensitive_file)`.
📝 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-02-10 - [SSH Key Creation Race Condition]
**Vulnerability:** SSH private keys were briefly world-readable during creation because `umask` was not restricted. The `op read ... > file` command created the file with default permissions (often 644/664), and `chmod 600` was only applied afterward.
**Learning:** Shell redirection creates files before `chmod` runs. This creates a Time-of-Check to Time-of-Use (TOCTOU) window where sensitive data is exposed.
**Prevention:** Always wrap sensitive file creation commands in a subshell with restricted umask: `(umask 077; command > sensitive_file)`. This ensures the file is born secure.
# Sentinel Journal
## 2026-02-10 - SSH Key Creation Race Condition
**Vulnerability:** SSH private keys were briefly world-readable during
creation because `umask` was not restricted. The `op read ... > file`
command created the file with default permissions (often 644/664), and
`chmod 600` was only applied afterward.
**Learning:** Shell redirection creates files before `chmod` runs. This
creates a TOCTOU window where sensitive data is exposed.
**Prevention:** Always wrap sensitive file creation in a subshell with
restricted umask: `(umask 077; command > sensitive_file)`.
🧰 Tools
🪛 GitHub Check: Lint Documentation

[failure] 4-4: Line length
.jules/sentinel.md:4:81 MD013/line-length Line length [Expected: 80; Actual: 176] 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: 159] 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: 251] 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-02-10 - [SSH Key Creat..."] 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-02-10 - [SSH Key Creation Race Condition]"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md

🤖 Prompt for AI Agents
In @.jules/sentinel.md around lines 1 - 4, Update the entry header and fix
markdownlint issues: change the heading "## 2024-02-10 - [SSH Key Creation Race
Condition]" to a top-level heading and correct the date to "2026-02-10" (e.g. "#
2026-02-10 - [SSH Key Creation Race Condition]"), ensure there is a blank line
after that heading, and reflow any lines longer than 80 characters (split the
"Vulnerability", "Learning", and "Prevention" lines into shorter
sentences/paragraphs) so the umask subshell recommendation "(umask 077; command
> sensitive_file)" and other text comply with MD013, MD022, and MD041.

Comment on lines +1 to +5
#!/bin/bash
set -e

# Setup test environment
TEST_DIR=$(mktemp -d)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a trap to ensure cleanup on failure.

With set -e, if any assertion fails the script exits immediately and rm -rf "$TEST_DIR" on line 85 is never reached, leaking temp directories.

Proposed fix
 #!/bin/bash
 set -e
 
 # Setup test environment
 TEST_DIR=$(mktemp -d)
+trap 'rm -rf "$TEST_DIR"' EXIT
 export HOME="$TEST_DIR"

And remove the manual cleanup at the end:

-# Cleanup
-rm -rf "$TEST_DIR"

Also applies to: 84-85

🤖 Prompt for AI Agents
In `@tests/test_ssh_creation.sh` around lines 1 - 5, Add a cleanup function that
removes TEST_DIR and register it with trap so it runs on exit/failure (e.g.,
define cleanup() { rm -rf "$TEST_DIR"; } and call trap cleanup EXIT or trap
cleanup ERR EXIT) to ensure temp dirs are removed when set -e causes early exit;
reference the TEST_DIR variable and remove the manual rm -rf "$TEST_DIR" at the
end of the script so the trap handles cleanup consistently.

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