-
Notifications
You must be signed in to change notification settings - Fork 1
π‘οΈ Sentinel: Fix TOCTOU race condition in SSH key creation #32
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| ## 2025-02-16 - TOCTOU Race Condition in Shell Scripts | ||
|
Check failure on line 1 in .jules/sentinel.md
|
||
| **Vulnerability:** Private SSH keys were created using redirection (`> file`) before restricting permissions (`chmod 600`), leaving a race window where keys were world-readable. | ||
|
Check failure on line 2 in .jules/sentinel.md
|
||
| **Learning:** Shell redirection creates files with default umask permissions (often 644) *before* any subsequent `chmod` command runs. | ||
|
Check failure on line 3 in .jules/sentinel.md
|
||
| **Prevention:** Use `(umask 077; command > file)` in a subshell to ensure files are created with restrictive permissions atomically. Also ensure existing files are removed before recreation. | ||
|
Check failure on line 4 in .jules/sentinel.md
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,62 @@ | ||||||||||||||||||||||||||||||||||||
| #!/bin/bash | ||||||||||||||||||||||||||||||||||||
| # Test for secure file creation logic | ||||||||||||||||||||||||||||||||||||
| set -e | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| TEST_FILE="tests/ssh_key_test" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Function to check permissions | ||||||||||||||||||||||||||||||||||||
| check_permissions() { | ||||||||||||||||||||||||||||||||||||
| local file="$1" | ||||||||||||||||||||||||||||||||||||
| local expected="$2" | ||||||||||||||||||||||||||||||||||||
| local perms | ||||||||||||||||||||||||||||||||||||
| if [[ "$(uname -s)" == "Darwin" ]]; then | ||||||||||||||||||||||||||||||||||||
| perms=$(stat -f "%Lp" "$file") | ||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||
| perms=$(stat -c "%a" "$file") | ||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| if [[ "$perms" != "$expected" ]]; then | ||||||||||||||||||||||||||||||||||||
| echo "FAIL: Permissions for $file are $perms, expected $expected" | ||||||||||||||||||||||||||||||||||||
| return 1 | ||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||
| echo "PASS: Permissions for $file are $perms" | ||||||||||||||||||||||||||||||||||||
| return 0 | ||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| echo "=== Test 1: Insecure Creation (Baseline) ===" | ||||||||||||||||||||||||||||||||||||
| rm -f "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
| # Standard creation (vulnerable pattern) | ||||||||||||||||||||||||||||||||||||
| echo "secret" > "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
| # Check if permissions are 644 (or 664 depending on umask) | ||||||||||||||||||||||||||||||||||||
| # Assuming default umask 022 -> 644 | ||||||||||||||||||||||||||||||||||||
| # We check if it is NOT 600 | ||||||||||||||||||||||||||||||||||||
| perms=$(stat -c "%a" "$TEST_FILE" 2>/dev/null || stat -f "%Lp" "$TEST_FILE") | ||||||||||||||||||||||||||||||||||||
| if [[ "$perms" != "600" ]]; then | ||||||||||||||||||||||||||||||||||||
| echo "PASS: Default creation is insecure ($perms)" | ||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||
| echo "WARN: Default umask is already strict (077)? This test assumes default umask allows group/other read." | ||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| echo "=== Test 2: Secure Creation (Fix Verification) ===" | ||||||||||||||||||||||||||||||||||||
| rm -f "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
| # Secure creation | ||||||||||||||||||||||||||||||||||||
| (umask 077; echo "secret" > "$TEST_FILE") | ||||||||||||||||||||||||||||||||||||
| check_permissions "$TEST_FILE" "600" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| echo "=== Test 3: Existing File (Regression Check) ===" | ||||||||||||||||||||||||||||||||||||
| rm -f "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
| # Create with 644 | ||||||||||||||||||||||||||||||||||||
| echo "old content" > "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
| chmod 644 "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| # Try to overwrite with umask 077 (simulating the fix applied blindly) | ||||||||||||||||||||||||||||||||||||
| (umask 077; echo "new content" > "$TEST_FILE") | ||||||||||||||||||||||||||||||||||||
| # Should still be 644 because file existed | ||||||||||||||||||||||||||||||||||||
| if check_permissions "$TEST_FILE" "644"; then | ||||||||||||||||||||||||||||||||||||
| echo "Confirmed: Overwriting existing file preserves insecure permissions (Must delete file first!)" | ||||||||||||||||||||||||||||||||||||
| else | ||||||||||||||||||||||||||||||||||||
| echo "Unexpected: Permissions changed to $perms?" | ||||||||||||||||||||||||||||||||||||
| fi | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+53
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: The Proposed fix # Try to overwrite with umask 077 (simulating the fix applied blindly)
(umask 077; echo "new content" > "$TEST_FILE")
# Should still be 644 because file existed
if check_permissions "$TEST_FILE" "644"; then
echo "Confirmed: Overwriting existing file preserves insecure permissions (Must delete file first!)"
else
- echo "Unexpected: Permissions changed to $perms?"
+ perms_actual=$(if [[ "$(uname -s)" == "Darwin" ]]; then stat -f "%Lp" "$TEST_FILE"; else stat -c "%a" "$TEST_FILE"; fi)
+ echo "Unexpected: Permissions changed to $perms_actual?"
fiπ Committable suggestion
Suggested change
π€ Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| rm -f "$TEST_FILE" | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix markdown lint failures flagged by CI.
The documentation lint check is failing with multiple issues:
#), not##.Also, the date reads 2025-02-16 β should this be 2026-02-16?
Proposed fix
π§° Tools
πͺ GitHub Check: Lint Documentation
[failure] 4-4: Line length
.jules/sentinel.md:4:81 MD013/line-length Line length [Expected: 80; Actual: 190] 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: 134] 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: 177] 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: "## 2025-02-16 - TOCTOU Race Co..."] 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: "## 2025-02-16 - TOCTOU Race Condition in Shell Scripts"] https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md
π€ Prompt for AI Agents