Conversation
Signed-off-by: Samuel K <skevetter@pm.me>
📝 WalkthroughWalkthroughChanges modify file and directory permissions across multiple components. Directory creation permissions are increased from 0755 to 0777 in most locations, with one kube config directory changed from 0700 to 0755. No control flow or public API modifications. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
cmd/helper/ssh_server.go (1)
119-133:⚠️ Potential issue | 🟡 MinorUnnecessary execute permission and inconsistency with daemon.go.
The activity file permissions changed from
0o666to0o777, adding an execute bit that has no meaning for a timestamp-tracking file. Additionally, this creates an inconsistency withcmd/agent/container/daemon.go:63-82, which creates the same file with0o666.While the security impact is minimal (the file is in
/tmpand meant to be touched by various processes), the inconsistency could cause confusion or unexpected behavior if permission checks are added later.Proposed fix to restore consistency
if err := os.WriteFile( agent.ContainerActivityFile, nil, - 0o777, + 0o666, ); err != nil { // `#nosec` G306 fmt.Fprintf(os.Stderr, "error writing file: %v\n", err) return } if err := os.Chmod( agent.ContainerActivityFile, - 0o777, + 0o666, ); err != nil { // `#nosec` G302🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@cmd/helper/ssh_server.go` around lines 119 - 133, The file creation sets overly permissive execute bits (0o777) and differs from daemon.go; change the file mode used in os.WriteFile and os.Chmod for agent.ContainerActivityFile to 0o666 (remove execute bits) to restore consistency with cmd/agent/container/daemon.go and ensure a non-executable, read/write-for-owner-and-group-and-others timestamp file; update the modes used in the os.WriteFile(...) and os.Chmod(...) calls around agent.ContainerActivityFile accordingly.pkg/devcontainer/setup/setup.go (1)
98-104:⚠️ Potential issue | 🟠 MajorWorld-writable result directory allows container config tampering.
The result directory (
/var/run/devpod) is now created with0o777. This directory containsresult.jsonwith sensitive container setup information. Any unprivileged process in the container could modify this file, potentially altering devcontainer configuration.Since the file itself is written with
0o600(Line 102), the directory permissions become the weak point. Consider using0o755to prevent unauthorized file creation/deletion while allowing read access.Proposed fix
- if err := os.MkdirAll(filepath.Dir(ResultLocation), 0o777); err != nil { // `#nosec` G301 + if err := os.MkdirAll(filepath.Dir(ResultLocation), 0o755); err != nil {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/devcontainer/setup/setup.go` around lines 98 - 104, The directory creation for ResultLocation uses world-writable mode 0o777; update the os.MkdirAll call that creates filepath.Dir(ResultLocation) to use 0o755 instead to prevent unauthorized writes. Locate the os.MkdirAll invocation in setup.go (around the ResultLocation usage) and change the permission argument from 0o777 to 0o755; keep the existing error logging via cfg.Log.Warnf and leave the os.WriteFile call writing ResultLocation with 0o600 unchanged.
🧹 Nitpick comments (1)
pkg/devcontainer/setup/setup.go (1)
176-178: World-writable /home directory is unconventional but may be intentional.Creating
/homewith0o777is unusual. Typically/homeis0o755to prevent users from creating arbitrary home directories. However, in containerized environments with dynamic user creation, this might be intentional.If this is needed for specific container workflows, consider documenting the rationale. Otherwise,
0o755would be more conventional.Suggested alternative if 0o777 is not required
- if err := os.MkdirAll("/home", 0o777); err != nil { // `#nosec` G301 + if err := os.MkdirAll("/home", 0o755); err != nil {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@pkg/devcontainer/setup/setup.go` around lines 176 - 178, The code creates /home with world-writable permissions via os.MkdirAll("/home", 0o777) — change the mode to 0o755 to follow conventional permissions (prevent arbitrary directory creation) and, if 0o777 is required for a container workflow, add an inline comment above the os.MkdirAll call documenting the specific container/user-creation rationale (or make the mode a named constant like homeDirMode and document it) so reviewers understand the intent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@pkg/agent/workspace.go`:
- Around line 151-154: The directory creation in isDirExecutable currently uses
os.MkdirAll(dir, 0o777) which makes the agent directory world-writable; change
this to use 0o755 so the directory is owner-writable and group/other
readable/executable (os.MkdirAll(dir, 0o755)), and ensure the subsequent test
file permission logic that sets the test file to 0o755 remains intact so the
script stays executable while preventing arbitrary users from modifying
directory contents.
In `@pkg/client/clientimplementation/proxy_client.go`:
- Around line 185-187: The workspace locks dir is being created with
world-writable permissions (os.MkdirAll(workspaceLocksDir, 0o777)) which breaks
flock-based mutual exclusion; change the permission mask to a user-restricted
value (prefer 0o700, or at minimum 0o755) when calling os.MkdirAll for
workspaceLocksDir (the path returned by GetLocksDir), so lock files remain
private to the user and cannot be manipulated by other local users.
In `@pkg/devcontainer/setup/setup.go`:
- Around line 363-368: The inline comment on the os.MkdirAll call for kubeDir
says "kube directory should be user-private" but the mode is 0o755
(world-readable); either change the file mode to 0o700 to make the directory
user-private or, if 0o755 is intentional, update the comment to reflect that it
is world-readable for tooling reasons; locate the os.MkdirAll(kubeDir, 0o755)
call and either replace the mode with 0o700 or revise the comment to remove
"user-private" and explain the chosen permission.
- Around line 427-430: The marker directory is created with world-writable
permissions (0o777), enabling attackers to delete or forge marker files; change
the directory creation mode in the block that calls os.MkdirAll for markerName
to a non-world-writable mode (e.g., 0o755 or 0o700) to prevent others from
creating/deleting files, and keep the marker file mode at 0o644; update any
related constants or comments and ensure this aligns with the existing
permission validation in shouldSkipKubeConfig so the logic is consistent (refer
to the os.MkdirAll call that uses markerName and the shouldSkipKubeConfig
function).
In `@pkg/ssh/server/agent.go`:
- Around line 24-29: The directory for per-reuse sockets (constructed from
reuseSock into dir) is currently created world-writable (0o777) which enables a
TOCTOU attack; change the MkdirAll call to create the directory with restrictive
permissions (e.g., 0o700 or 0o755—prefer 0o700) and ensure ownership is set
immediately (chownListener is used later) before any socket is created inside it
so a local user cannot replace the socket between creation and chown; update the
MkdirAll permissions for dir and ensure the code path that creates the socket
performs chownListener (or equivalent) immediately after directory creation and
before binding the socket.
---
Outside diff comments:
In `@cmd/helper/ssh_server.go`:
- Around line 119-133: The file creation sets overly permissive execute bits
(0o777) and differs from daemon.go; change the file mode used in os.WriteFile
and os.Chmod for agent.ContainerActivityFile to 0o666 (remove execute bits) to
restore consistency with cmd/agent/container/daemon.go and ensure a
non-executable, read/write-for-owner-and-group-and-others timestamp file; update
the modes used in the os.WriteFile(...) and os.Chmod(...) calls around
agent.ContainerActivityFile accordingly.
In `@pkg/devcontainer/setup/setup.go`:
- Around line 98-104: The directory creation for ResultLocation uses
world-writable mode 0o777; update the os.MkdirAll call that creates
filepath.Dir(ResultLocation) to use 0o755 instead to prevent unauthorized
writes. Locate the os.MkdirAll invocation in setup.go (around the ResultLocation
usage) and change the permission argument from 0o777 to 0o755; keep the existing
error logging via cfg.Log.Warnf and leave the os.WriteFile call writing
ResultLocation with 0o600 unchanged.
---
Nitpick comments:
In `@pkg/devcontainer/setup/setup.go`:
- Around line 176-178: The code creates /home with world-writable permissions
via os.MkdirAll("/home", 0o777) — change the mode to 0o755 to follow
conventional permissions (prevent arbitrary directory creation) and, if 0o777 is
required for a container workflow, add an inline comment above the os.MkdirAll
call documenting the specific container/user-creation rationale (or make the
mode a named constant like homeDirMode and document it) so reviewers understand
the intent.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 528e9a21-6ec5-41dc-94ef-1153f595b890
📒 Files selected for processing (5)
cmd/helper/ssh_server.gopkg/agent/workspace.gopkg/client/clientimplementation/proxy_client.gopkg/devcontainer/setup/setup.gopkg/ssh/server/agent.go
| // #nosec G301,G703 -- TODO Consider using a more secure permission setting and ownership if needed. | ||
| if err := os.MkdirAll(dir, 0o755); err != nil { | ||
| if err := os.MkdirAll(dir, 0o777); err != nil { | ||
| return false, err | ||
| } |
There was a problem hiding this comment.
World-writable agent directory may enable tampering.
The isDirExecutable function now creates the agent directory with 0o777 permissions. While the #nosec comment acknowledges the concern, a world-writable directory allows any local user to place malicious scripts or modify agent binaries in shared environments.
If the intent is to ensure the test script can execute regardless of umask, consider creating the directory with 0o755 and explicitly setting the test file to 0o755 (which is already done on Line 160). The directory only needs to be writable by the owner.
Proposed fix
// `#nosec` G301,G703 -- TODO Consider using a more secure permission setting and ownership if needed.
- if err := os.MkdirAll(dir, 0o777); err != nil {
+ if err := os.MkdirAll(dir, 0o755); err != nil {📝 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.
| // #nosec G301,G703 -- TODO Consider using a more secure permission setting and ownership if needed. | |
| if err := os.MkdirAll(dir, 0o755); err != nil { | |
| if err := os.MkdirAll(dir, 0o777); err != nil { | |
| return false, err | |
| } | |
| // `#nosec` G301,G703 -- TODO Consider using a more secure permission setting and ownership if needed. | |
| if err := os.MkdirAll(dir, 0o755); err != nil { | |
| return false, err | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/agent/workspace.go` around lines 151 - 154, The directory creation in
isDirExecutable currently uses os.MkdirAll(dir, 0o777) which makes the agent
directory world-writable; change this to use 0o755 so the directory is
owner-writable and group/other readable/executable (os.MkdirAll(dir, 0o755)),
and ensure the subsequent test file permission logic that sets the test file to
0o755 remains intact so the script stays executable while preventing arbitrary
users from modifying directory contents.
| if err = os.MkdirAll(workspaceLocksDir, 0o777); err != nil { // #nosec G301 | ||
| panic(fmt.Errorf("create workspace locks dir: %w", err)) | ||
| } |
There was a problem hiding this comment.
World-writable lock directory breaks mutual exclusion guarantees.
Changing the locks directory from 0o755 to 0o777 allows any local user on a multi-user system to delete or manipulate lock files, defeating the purpose of flock-based mutual exclusion. This could lead to race conditions where multiple DevPod processes operate on the same workspace simultaneously, causing data corruption.
As noted in the relevant context, GetLocksDir returns a path within the user's home directory (~/.devpod/contexts/{context}/locks), which should remain user-private. Consider keeping 0o755 or using 0o700 for stricter access control.
Proposed fix
- if err = os.MkdirAll(workspaceLocksDir, 0o777); err != nil { // `#nosec` G301
+ if err = os.MkdirAll(workspaceLocksDir, 0o755); err != nil {📝 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.
| if err = os.MkdirAll(workspaceLocksDir, 0o777); err != nil { // #nosec G301 | |
| panic(fmt.Errorf("create workspace locks dir: %w", err)) | |
| } | |
| if err = os.MkdirAll(workspaceLocksDir, 0o755); err != nil { | |
| panic(fmt.Errorf("create workspace locks dir: %w", err)) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/client/clientimplementation/proxy_client.go` around lines 185 - 187, The
workspace locks dir is being created with world-writable permissions
(os.MkdirAll(workspaceLocksDir, 0o777)) which breaks flock-based mutual
exclusion; change the permission mask to a user-restricted value (prefer 0o700,
or at minimum 0o755) when calling os.MkdirAll for workspaceLocksDir (the path
returned by GetLocksDir), so lock files remain private to the user and cannot be
manipulated by other local users.
| if err := os.MkdirAll( | ||
| kubeDir, | ||
| 0o700, | ||
| 0o755, | ||
| ); err != nil { // #nosec G301 -- kube directory should be user-private | ||
| return err | ||
| } |
There was a problem hiding this comment.
Comment-code mismatch: comment says "user-private" but 0o755 allows world-read.
The inline comment states "kube directory should be user-private," but 0o755 allows any user to read the directory contents. If this is intentional (e.g., changed from 0o700 to support certain tooling), consider updating the comment. If the directory should truly be private, use 0o700.
Option A: Update comment to match permissions
if err := os.MkdirAll(
kubeDir,
0o755,
- ); err != nil { // `#nosec` G301 -- kube directory should be user-private
+ ); err != nil { // `#nosec` G301 -- kube directory is user-writable, world-readableOption B: Restore private permissions to match comment
if err := os.MkdirAll(
kubeDir,
- 0o755,
- ); err != nil { // `#nosec` G301 -- kube directory should be user-private
+ 0o700,
+ ); err != nil { // kube directory should be user-private📝 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.
| if err := os.MkdirAll( | |
| kubeDir, | |
| 0o700, | |
| 0o755, | |
| ); err != nil { // #nosec G301 -- kube directory should be user-private | |
| return err | |
| } | |
| if err := os.MkdirAll( | |
| kubeDir, | |
| 0o755, | |
| ); err != nil { // `#nosec` G301 -- kube directory is user-writable, world-readable | |
| return err | |
| } |
| if err := os.MkdirAll( | |
| kubeDir, | |
| 0o700, | |
| 0o755, | |
| ); err != nil { // #nosec G301 -- kube directory should be user-private | |
| return err | |
| } | |
| if err := os.MkdirAll( | |
| kubeDir, | |
| 0o700, | |
| ); err != nil { // kube directory should be user-private | |
| return err | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/devcontainer/setup/setup.go` around lines 363 - 368, The inline comment
on the os.MkdirAll call for kubeDir says "kube directory should be user-private"
but the mode is 0o755 (world-readable); either change the file mode to 0o700 to
make the directory user-private or, if 0o755 is intentional, update the comment
to reflect that it is world-readable for tooling reasons; locate the
os.MkdirAll(kubeDir, 0o755) call and either replace the mode with 0o700 or
revise the comment to remove "user-private" and explain the chosen permission.
| _ = os.MkdirAll(filepath.Dir(markerName), 0o777) // #nosec G301 | ||
| if err := os.WriteFile(markerName, []byte(markerContent), 0o644); err != nil { // #nosec G306 | ||
| return false, fmt.Errorf("write marker: %w", err) | ||
| } |
There was a problem hiding this comment.
World-writable marker directory enables setup bypass/replay attacks.
The marker directory (/var/devpod) is now created with 0o777. Marker files control whether setup steps are skipped on subsequent runs. A malicious process could:
- Delete markers to force re-execution of setup steps
- Create fake markers to skip legitimate setup
Combined with the permission check in shouldSkipKubeConfig (Lines 339-346) that warns about insecure marker permissions, this change seems contradictory. The marker file itself uses 0o644, but the directory permissions undermine this.
Proposed fix
- _ = os.MkdirAll(filepath.Dir(markerName), 0o777) // `#nosec` G301
+ _ = os.MkdirAll(filepath.Dir(markerName), 0o755)📝 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.
| _ = os.MkdirAll(filepath.Dir(markerName), 0o777) // #nosec G301 | |
| if err := os.WriteFile(markerName, []byte(markerContent), 0o644); err != nil { // #nosec G306 | |
| return false, fmt.Errorf("write marker: %w", err) | |
| } | |
| _ = os.MkdirAll(filepath.Dir(markerName), 0o755) | |
| if err := os.WriteFile(markerName, []byte(markerContent), 0o644); err != nil { // `#nosec` G306 | |
| return false, fmt.Errorf("write marker: %w", err) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/devcontainer/setup/setup.go` around lines 427 - 430, The marker directory
is created with world-writable permissions (0o777), enabling attackers to delete
or forge marker files; change the directory creation mode in the block that
calls os.MkdirAll for markerName to a non-world-writable mode (e.g., 0o755 or
0o700) to prevent others from creating/deleting files, and keep the marker file
mode at 0o644; update any related constants or comments and ensure this aligns
with the existing permission validation in shouldSkipKubeConfig so the logic is
consistent (refer to the os.MkdirAll call that uses markerName and the
shouldSkipKubeConfig function).
| if reuseSock != "" { | ||
| dir = filepath.Join(os.TempDir(), fmt.Sprintf("auth-agent-%s", reuseSock)) | ||
| // #nosec G301 -- TODO Consider using a more secure permission setting and ownership if needed. | ||
| err = os.MkdirAll(dir, 0o755) | ||
| err = os.MkdirAll(dir, 0o777) // #nosec G301 | ||
| if err != nil { | ||
| return nil, "", fmt.Errorf("creating SSH_AUTH_SOCK dir in /tmp: %w", err) | ||
| } |
There was a problem hiding this comment.
World-writable socket directory enables SSH agent hijacking.
The per-reuse socket directory (auth-agent-{reuseSock}) is now created with 0o777. Combined with the chownListener function (in ssh_container.go:150-165), this creates a TOCTOU vulnerability:
- Directory created with world-writable permissions
- Socket created inside
chownListenerchanges ownership
Between steps 2 and 3, any local user can delete and replace the socket, potentially intercepting SSH agent connections. Since this socket is used for browser tunnels (openvscode IDE), this could expose SSH credentials.
Consider using 0o755 or 0o700 to restrict directory access before the chown occurs.
Proposed fix
if reuseSock != "" {
dir = filepath.Join(os.TempDir(), fmt.Sprintf("auth-agent-%s", reuseSock))
- err = os.MkdirAll(dir, 0o777) // `#nosec` G301
+ err = os.MkdirAll(dir, 0o700) // `#nosec` G301📝 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.
| if reuseSock != "" { | |
| dir = filepath.Join(os.TempDir(), fmt.Sprintf("auth-agent-%s", reuseSock)) | |
| // #nosec G301 -- TODO Consider using a more secure permission setting and ownership if needed. | |
| err = os.MkdirAll(dir, 0o755) | |
| err = os.MkdirAll(dir, 0o777) // #nosec G301 | |
| if err != nil { | |
| return nil, "", fmt.Errorf("creating SSH_AUTH_SOCK dir in /tmp: %w", err) | |
| } | |
| if reuseSock != "" { | |
| dir = filepath.Join(os.TempDir(), fmt.Sprintf("auth-agent-%s", reuseSock)) | |
| err = os.MkdirAll(dir, 0o700) // `#nosec` G301 | |
| if err != nil { | |
| return nil, "", fmt.Errorf("creating SSH_AUTH_SOCK dir in /tmp: %w", err) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@pkg/ssh/server/agent.go` around lines 24 - 29, The directory for per-reuse
sockets (constructed from reuseSock into dir) is currently created
world-writable (0o777) which enables a TOCTOU attack; change the MkdirAll call
to create the directory with restrictive permissions (e.g., 0o700 or
0o755—prefer 0o700) and ensure ownership is set immediately (chownListener is
used later) before any socket is created inside it so a local user cannot
replace the socket between creation and chown; update the MkdirAll permissions
for dir and ensure the code path that creates the socket performs chownListener
(or equivalent) immediately after directory creation and before binding the
socket.
Signed-off-by: Samuel K skevetter@pm.me
Summary by CodeRabbit