-
-
Notifications
You must be signed in to change notification settings - Fork 308
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
Fix windows permissions #1766
base: develop
Are you sure you want to change the base?
Fix windows permissions #1766
Conversation
📝 WalkthroughWalkthroughThis pull request introduces changes primarily focused on enhancing the management of file and directory permissions across various components of the codebase. It replaces hardcoded permission values with constants from the Changes
Possibly related PRs
Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration 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.
Actionable comments posted: 9
🧹 Outside diff range and nitpick comments (4)
base/utils/permissions.go (3)
1-2
: Move build constraint to package lineThe build constraint should be on the same line as the package declaration for better readability and consistency with Go conventions.
-//go:build !windows - -package utils +//go:build !windows +package utils
12-15
: Clarify SetExecPermission implementationThe function reuses
SetDirPermission
logic, but it's not immediately clear why. Consider adding a comment explaining that executable files require the same permission bits as directories on Unix systems.// SetExecPermission sets the permission of an executable file. +// Note: On Unix systems, executable files require the same execute permission bits as directories, +// hence reusing the directory permission logic. func SetExecPermission(path string, perm FSPermission) error { return SetDirPermission(path, perm) }
7-20
: Consider consistent error wrappingAll three functions directly return errors from os.Chmod. Consider wrapping these errors with additional context about the operation being performed.
func SetDirPermission(path string, perm FSPermission) error { - return os.Chmod(path, perm.AsUnixDirExecPermission()) + if err := os.Chmod(path, perm.AsUnixDirExecPermission()); err != nil { + return fmt.Errorf("failed to set directory permissions on %s: %w", path, err) + } + return nil }base/utils/fs.go (1)
42-46
: Inconsistent error handling between platformsThe function handles permissions differently on Windows vs Unix systems. Consider unifying the approach to make the behavior more predictable.
// Set windows permissions. Linux permission where already set with creation. if isWindows { - // Ignore windows permission error. For none admin users it will always fail. - SetDirPermission(path, perm) + if err := SetDirPermission(path, perm); err != nil { + // Log but don't fail for backward compatibility + log.Warningf("Failed to set directory permissions on Windows: %v", err) + } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (18)
base/config/main.go
(1 hunks)base/database/main.go
(3 hunks)base/database/storage/fstree/fstree.go
(1 hunks)base/dataroot/root.go
(1 hunks)base/updater/fetch.go
(2 hunks)base/updater/registry.go
(1 hunks)base/utils/fs.go
(3 hunks)base/utils/permissions.go
(1 hunks)base/utils/permissions_windows.go
(1 hunks)base/utils/structure.go
(2 hunks)cmds/portmaster-start/dirs.go
(2 hunks)cmds/portmaster-start/main.go
(2 hunks)service/core/base/global.go
(2 hunks)service/netquery/database.go
(3 hunks)service/profile/module.go
(2 hunks)service/ui/module.go
(2 hunks)service/updates/main.go
(2 hunks)service/updates/upgrader.go
(2 hunks)
✅ Files skipped from review due to trivial changes (1)
- service/netquery/database.go
🔇 Additional comments (20)
cmds/portmaster-start/dirs.go (2)
8-8
: LGTM: Import added correctly
The utils import is correctly added and organized with the standard library imports.
28-28
: Review security implications of PublicWritePermission
Using PublicWritePermission
for the exec directory might be too permissive and could pose security risks. Consider:
- Using a more restrictive permission level
- Documenting why public write access is necessary
- Adding validation checks for the directory contents
✅ Verification successful
Let me generate additional scripts to better understand the security implications.
Let me try one more search to find the definition and usage context of PublicWritePermission.
Based on the findings, I can now generate the final response:
Public write permissions are appropriate for the exec directory
The exec directory is used as a temporary directory that gets cleaned up and recreated during startup. The code shows that:
- The directory is completely removed (
os.RemoveAll
) before use - It's recreated fresh on every start
- This cleanup-recreate cycle helps maintain security by ensuring no stale content persists
The public write permission is necessary for this temporary execution directory to function properly, and the security implications are properly mitigated through the cleanup process.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for other uses of PublicWritePermission to understand the pattern
rg "PublicWritePermission"
# Check if there are any security-related comments or documentation about exec directory permissions
rg -A 5 "exec.*directory"
Length of output: 66101
Script:
#!/bin/bash
# Check for any security-related comments or documentation about permissions
rg -i "permission.*security|security.*permission" -A 3
# Look for the definition of PublicWritePermission
ast-grep --pattern 'PublicWritePermission = $_'
# Check for any documentation about the exec directory
fd -e md -e txt . -X grep -l -i "exec.*directory"
Length of output: 2379
Script:
#!/bin/bash
# Look for the definition and usage of PublicWritePermission in utils package
rg -A 5 "type.*Permission|const.*Permission|var.*Permission" base/utils/
# Check how the exec directory is used
rg -A 5 "execDir"
Length of output: 1255
service/core/base/global.go (1)
58-58
: Verify permission requirements for data directory
The change to utils.PublicReadPermission
seems appropriate as it maintains read access while restricting write access. However, please verify if any components require write access to this directory.
✅ Verification successful
Based on the search results, I can now generate a final response:
Permission change to utils.PublicReadPermission
is appropriate
The change to utils.PublicReadPermission
for the data directory is correct because:
- The data directory primarily needs read access for most components
- Write operations to specific subdirectories are handled separately with appropriate permissions:
exec
directory usesPublicWritePermission
databases
directory usesAdminOnlyPermission
- Other subdirectories explicitly set their required permissions when created
The codebase shows a well-structured permission model where:
- The root data directory maintains public read access
- Subdirectories that need different permissions are explicitly created with their specific permission requirements
- No direct write operations are performed on the root data directory itself
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for write operations to data directory
rg "dataDir.*Write|dataDir.*Create|dataDir.*Remove" --type go
Length of output: 63
Script:
#!/bin/bash
# Let's try a broader search for dataDir usage and dataroot package functionality
echo "=== Searching for dataDir usage ==="
rg "dataDir" -A 3 -B 3 --type go
echo -e "\n=== Searching for dataroot package usage ==="
rg "dataroot\." -A 3 -B 3 --type go
echo -e "\n=== Looking for Initialize function implementation ==="
ast-grep --pattern 'func Initialize($$$) {
$$$
}'
Length of output: 17123
base/utils/fs.go (1)
38-41
: Verify directory creation permissions
The initial directory creation uses Unix-style permissions even on Windows. Verify this doesn't cause issues with subsequent Windows ACL application.
✅ Verification successful
Windows permissions are properly handled
The code already implements special handling for Windows permissions:
- Unix permissions are used for initial directory creation on all platforms
- On Windows, there's explicit handling to:
- Ignore permission errors for non-admin users
- Apply Windows-specific ACLs after directory creation
- Skip permission checks during directory existence verification
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for potential permission-related issues in error handling
rg "os\.Mkdir.*permission|os\.MkdirAll.*permission" --type go
Length of output: 63
Script:
#!/bin/bash
# Let's check how the permission is defined and used across the codebase
rg "AsUnixDirExecPermission" -A 3 --type go
# Also check for Windows-specific permission handling
rg "windows.*permission|acl.*permission" --type go
# Look at the actual implementation of the fs.go file
cat base/utils/fs.go
Length of output: 3197
service/ui/module.go (2)
10-10
: LGTM: Import added for utils package
The addition of the utils import is appropriate for using the permission constants.
31-31
: Verify parent directory permissions
The change from hardcoded 0o0777
to utils.PublicWritePermission
is good for cross-platform compatibility. However, since this is a security-sensitive directory used for process execution, we should verify the parent directory permissions are properly restrictive.
✅ Verification successful
Let me analyze the results and generate one more verification to check the permission constants and their values.
Parent directory permissions are properly secured
The verification shows that the permission model is well-designed and secure:
- The parent directory "databases" uses
AdminOnlyPermission
(0o700 on Unix, admin-only on Windows) - The "exec" directory's
PublicWritePermission
is intentional as it needs to be writable for process execution - The permission constants are consistently used across the codebase with clear security boundaries:
AdminOnlyPermission
: Used for sensitive data (0o700)PublicReadPermission
: Used for shared resources (0o755)PublicWritePermission
: Used only where necessary (0o777)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check parent directory permissions and usage of exec dir
# Look for any file operations or directory access in the exec directory
rg -A 3 "exec.*dir" --type go
# Search for other instances of dataroot operations to verify consistent permission usage
ast-grep --pattern 'dataroot.Root().ChildDir($_, $_)'
Length of output: 1967
Script:
#!/bin/bash
# Check the permission constants defined in utils
rg -A 3 "PublicWritePermission|PublicReadPermission|AdminOnlyPermission" --type go
# Look for any security-related comments or documentation about these permissions
rg -i "permission.*constant|directory.*permission" --type go
Length of output: 7826
base/database/main.go (3)
28-28
: LGTM: Appropriate permission for root directory
The change to utils.PublicReadPermission
is suitable for the root directory while maintaining Windows compatibility.
37-37
: LGTM: Enhanced security with AdminOnlyPermission
Good use of utils.AdminOnlyPermission
for the databases directory, providing proper protection for sensitive database files.
70-70
: LGTM: Consistent use of AdminOnlyPermission
Appropriate and consistent use of utils.AdminOnlyPermission
for database storage locations, maintaining proper access restrictions.
base/config/main.go (1)
147-149
: LGTM: Consistent permission usage in test setup
Good consistency in using utils.PublicReadPermission
for both the directory structure and dataroot initialization. This ensures proper cross-platform compatibility in test environments.
base/utils/structure.go (1)
11-17
: LGTM: Clear and well-structured permission constants
The introduction of FSPermission
type with three distinct levels (AdminOnly, PublicRead, PublicWrite) provides a clear and type-safe way to handle permissions.
service/profile/module.go (1)
74-74
: LGTM: Appropriate use of AdminOnlyPermission
The change from hardcoded 0o700
to utils.AdminOnlyPermission
for the icons directory is appropriate as it contains user profile data that should be protected.
cmds/portmaster-start/main.go (2)
182-182
: LGTM: Appropriate permission levels for data directories
The use of PublicReadPermission
for both the data root and updates directory is appropriate as these directories need to be readable by all users but should not be writable.
Also applies to: 189-189
199-199
: Verify logging permissions on Windows
The use of PublicWritePermission
for logs is necessary for all users to write logs, but we should verify this works correctly on Windows systems.
✅ Verification successful
The previous results show Windows-specific permission handling exists in the codebase, but we need to specifically verify how logs are handled and how PublicWritePermission is defined and used for logging. This will help us determine if the Windows permission handling is adequate for the logging scenario.
The previous results show the logging setup but we need to understand how PublicWritePermission is defined and how it's handled by DirStructure, especially on Windows. This will help us verify if the permission model is appropriate for Windows systems.
The previous results show that Windows permission errors are explicitly ignored for non-admin users, and we need to verify the specific FSPermission constants and their handling to understand the complete picture of log file permissions on Windows.
Based on the comprehensive results, I can now generate the final response:
Windows log permissions are properly handled with fallback mechanisms
The codebase has specific handling for Windows permissions:
- Windows permission errors are explicitly ignored for non-admin users with a fallback mechanism
- The
utils.PublicWritePermission
is applied through Windows-specific ACL handling - The codebase includes special cases for Windows in
base/utils/fs.go
that ensure directory access works even when permission setting fails
The verification shows that the logging permissions are handled appropriately on Windows through:
- Windows-specific permission handling in
base/utils/permissions_windows.go
- Fallback mechanisms that prevent permission issues from blocking logging functionality
- Explicit error handling that allows non-admin users to still write logs even when they can't set permissions
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for any Windows-specific permission handling in the codebase
rg -l "windows" --type go | xargs rg "permission|FileMode" -B 2 -A 2
# Look for any existing log permission issues
rg -i "permission.*denied.*log" -g "!*.git"
Length of output: 7414
Script:
#!/bin/bash
# Check for Windows-specific log handling and permissions
rg -B 3 -A 3 "logs.*windows|windows.*logs" --type go
# Check how PublicWritePermission is defined and used
ast-grep --pattern 'const PublicWritePermission'
# Look for log-related permission handling
rg "logsRoot|logRoot" -B 2 -A 2 --type go
Length of output: 1906
Script:
#!/bin/bash
# Check how PublicWritePermission is defined
rg "type.*Permission|const.*Permission" -B 2 -A 2 --type go
# Check DirStructure implementation for permission handling
ast-grep --pattern 'type DirStructure struct'
rg "func.*DirStructure.*Ensure" -A 5 --type go
# Check for any Windows-specific error handling related to permissions
rg "windows.*permission.*error|permission.*error.*windows" --type go
Length of output: 2571
Script:
#!/bin/bash
# Check FSPermission constants and methods
rg "const.*Permission.*=|func.*Permission" -B 2 -A 2 --type go
# Check for any log-related error handling or issues
rg "failed.*log|error.*log" -B 2 -A 2 --type go
# Look for Windows-specific directory handling
ast-grep --pattern 'if runtime.GOOS == "windows"'
Length of output: 28650
base/updater/registry.go (1)
101-101
: LGTM! Verify Windows behavior.
The change from hardcoded permission to utils.AdminOnlyPermission
is appropriate for a temporary directory that should only be accessible by administrators.
Let's verify the Windows-specific behavior:
✅ Verification successful
Windows-specific behavior for AdminOnlyPermission is properly implemented
The verification shows that AdminOnlyPermission
has a dedicated Windows implementation in base/utils/permissions_windows.go
that correctly sets Windows ACLs to grant full access only to the Administrators group, which is the appropriate behavior for a temporary directory that should only be accessible by administrators. The implementation:
- Uses Windows-specific ACL management
- Grants
GENERIC_ALL|STANDARD_RIGHTS_ALL
permissions only to the "Administrators" group - Removes all other permissions
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for Windows-specific permission handling
# Test: Look for Windows-specific permission handling in utils package
rg -A 5 "AdminOnlyPermission.*windows" || ast-grep --pattern 'const AdminOnlyPermission = $_'
Length of output: 94
Script:
#!/bin/bash
# Let's first find the definition of AdminOnlyPermission
rg -l "AdminOnlyPermission"
# Then check its implementation and any Windows-specific handling
rg -A 5 "AdminOnlyPermission"
Length of output: 4181
service/updates/main.go (2)
16-16
: LGTM!
The addition of the utils
import is necessary for using the permission constants.
142-142
: LGTM! Verify update directory accessibility.
The change to utils.PublicReadPermission
is appropriate as updates should be readable by all users.
Let's verify the permission constant usage:
✅ Verification successful
Permission usage for updates directory is consistent and appropriate
The verification confirms that utils.PublicReadPermission
is consistently used across the codebase for the updates directory and related operations:
- In
service/updates/main.go
andcmds/portmaster-start/main.go
for initializing the updates directory - In
service/updates/upgrader.go
for setting permissions on upgraded files - The permission translates to 0o755 on Unix-like systems and appropriate ACLs on Windows
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for consistent permission usage
# Test: Look for other usages of PublicReadPermission in update-related code
rg "PublicReadPermission.*updates" || ast-grep --pattern 'ChildDir($_, utils.PublicReadPermission)'
Length of output: 100
Script:
#!/bin/bash
# Let's check for any usage of PublicReadPermission and permission-related constants in the codebase
rg "PublicReadPermission" -A 2
# Also check for other permission constants used with updates directory
rg "updates.*Permission|Permission.*updates"
# Let's see the actual constant definition
ast-grep --pattern 'const PublicReadPermission'
Length of output: 3002
service/updates/upgrader.go (2)
23-23
: LGTM: Import change aligns with new permission handling.
The replacement of the go-acl
package with the internal utils
package centralizes permission management.
354-354
: Verify permission consistency across platforms.
The new permission handling looks good but requires verification:
- Windows: Uses
utils.SetExecPermission
withPublicReadPermission
- Unix: Checks current permissions and updates if needed using
AsUnixDirExecPermission()
Also applies to: 356-365
✅ Verification successful
Based on the search results, I can now generate the final response:
Permission handling is consistent across platforms with appropriate platform-specific implementations
The verification confirms:
- Windows: Uses ACL-based permissions via
permissions_windows.go
with admin rights and read/execute for users - Unix: Uses standard Unix permissions (0o755) via
structure.go
- All calls to
SetExecPermission
consistently usePublicReadPermission
- The implementation is properly separated into platform-specific files with identical public APIs
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify consistent permission handling across the codebase
# Test: Search for all uses of SetExecPermission to ensure consistent permission levels
# Check for all SetExecPermission calls
rg "SetExecPermission\(" -A 2
# Check for all PublicReadPermission usage
rg "PublicReadPermission" -A 2
Length of output: 3802
base/updater/fetch.go (1)
20-20
: LGTM: Import change aligns with new permission handling.
The replacement of the go-acl
package with the internal utils
package centralizes permission management.
|
||
"github.com/safing/portmaster/base/utils" | ||
) | ||
|
||
var root *utils.DirStructure | ||
|
||
// Initialize initializes the data root directory. | ||
func Initialize(rootDir string, perm os.FileMode) error { | ||
func Initialize(rootDir string, perm utils.FSPermission) error { |
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.
💡 Codebase verification
Update permission value in notifier/main.go to use utils.FSPermission
The verification shows that most callers have been updated to use utils.FSPermission
, but there's one remaining instance in cmds/notifier/main.go
that still uses a raw octal permission value (0o0755
). This needs to be updated for consistency.
cmds/notifier/main.go
: Replace0o0755
with appropriateutils.FSPermission
constant
🔗 Analysis chain
Verify all callers of Initialize function
The parameter type change from os.FileMode
to utils.FSPermission
is good for cross-platform compatibility. However, we should verify that all callers have been updated accordingly.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for any remaining direct calls using os.FileMode
rg -A 2 "Initialize\([^,]+,\s*os\."
# Search for all calls to Initialize to verify they've been updated
rg -A 2 "dataroot\.Initialize\("
Length of output: 897
base/utils/permissions_windows.go
Outdated
func SetDirPermission(path string, perm FSPermission) error { | ||
setWindowsFilePermissions(path, perm) | ||
return nil | ||
} |
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.
Critical: Add error handling for ACL operations
The function ignores all errors from setWindowsFilePermissions
. This could silently fail to set permissions, leading to security vulnerabilities.
func SetDirPermission(path string, perm FSPermission) error {
- setWindowsFilePermissions(path, perm)
- return nil
+ return setWindowsFilePermissions(path, perm)
}
📝 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.
func SetDirPermission(path string, perm FSPermission) error { | |
setWindowsFilePermissions(path, perm) | |
return nil | |
} | |
func SetDirPermission(path string, perm FSPermission) error { | |
return setWindowsFilePermissions(path, perm) | |
} |
base/utils/permissions_windows.go
Outdated
func setWindowsFilePermissions(path string, perm FSPermission) { | ||
switch perm { | ||
case AdminOnlyPermission: | ||
// Set only admin rights, remove all others. | ||
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | ||
case PublicReadPermission: | ||
// Set admin rights and read/execute rights for users, remove all others. | ||
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | ||
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_EXECUTE, "Users")) | ||
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_READ, "Users")) | ||
case PublicWritePermission: | ||
// Set full control to admin and regular users. Guest users will not have access. | ||
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | ||
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users")) | ||
} | ||
} |
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.
Critical: Improve error handling in setWindowsFilePermissions
- The function doesn't handle errors from
acl.Apply
- Multiple
acl.Apply
calls could partially succeed, leaving inconsistent permissions - No path validation against directory traversal attacks
-func setWindowsFilePermissions(path string, perm FSPermission) {
+func setWindowsFilePermissions(path string, perm FSPermission) error {
+ // Validate path to prevent directory traversal
+ cleanPath, err := filepath.Abs(filepath.Clean(path))
+ if err != nil {
+ return fmt.Errorf("invalid path: %w", err)
+ }
+
switch perm {
case AdminOnlyPermission:
- acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators"))
+ if err := acl.Apply(cleanPath, true, false,
+ acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil {
+ return fmt.Errorf("failed to set admin-only permissions: %w", err)
+ }
case PublicReadPermission:
- acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators"))
- acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_EXECUTE, "Users"))
- acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_READ, "Users"))
+ if err := acl.Apply(cleanPath, true, false,
+ acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil {
+ return fmt.Errorf("failed to set admin permissions: %w", err)
+ }
+ if err := acl.Apply(cleanPath, false, false,
+ acl.GrantName(windows.GENERIC_EXECUTE|windows.GENERIC_READ, "Users")); err != nil {
+ return fmt.Errorf("failed to set user permissions: %w", err)
+ }
case PublicWritePermission:
- acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators"))
- acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users"))
+ if err := acl.Apply(cleanPath, true, false,
+ acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil {
+ return fmt.Errorf("failed to set admin permissions: %w", err)
+ }
+ if err := acl.Apply(cleanPath, false, false,
+ acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users")); err != nil {
+ return fmt.Errorf("failed to set user permissions: %w", err)
+ }
}
+ return 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.
func setWindowsFilePermissions(path string, perm FSPermission) { | |
switch perm { | |
case AdminOnlyPermission: | |
// Set only admin rights, remove all others. | |
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | |
case PublicReadPermission: | |
// Set admin rights and read/execute rights for users, remove all others. | |
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | |
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_EXECUTE, "Users")) | |
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_READ, "Users")) | |
case PublicWritePermission: | |
// Set full control to admin and regular users. Guest users will not have access. | |
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | |
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users")) | |
} | |
} | |
func setWindowsFilePermissions(path string, perm FSPermission) error { | |
// Validate path to prevent directory traversal | |
cleanPath, err := filepath.Abs(filepath.Clean(path)) | |
if err != nil { | |
return fmt.Errorf("invalid path: %w", err) | |
} | |
switch perm { | |
case AdminOnlyPermission: | |
if err := acl.Apply(cleanPath, true, false, | |
acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil { | |
return fmt.Errorf("failed to set admin-only permissions: %w", err) | |
} | |
case PublicReadPermission: | |
if err := acl.Apply(cleanPath, true, false, | |
acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil { | |
return fmt.Errorf("failed to set admin permissions: %w", err) | |
} | |
if err := acl.Apply(cleanPath, false, false, | |
acl.GrantName(windows.GENERIC_EXECUTE|windows.GENERIC_READ, "Users")); err != nil { | |
return fmt.Errorf("failed to set user permissions: %w", err) | |
} | |
case PublicWritePermission: | |
if err := acl.Apply(cleanPath, true, false, | |
acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")); err != nil { | |
return fmt.Errorf("failed to set admin permissions: %w", err) | |
} | |
if err := acl.Apply(cleanPath, false, false, | |
acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users")); err != nil { | |
return fmt.Errorf("failed to set user permissions: %w", err) | |
} | |
} | |
return nil | |
} |
base/utils/permissions_windows.go
Outdated
case PublicWritePermission: | ||
// Set full control to admin and regular users. Guest users will not have access. | ||
acl.Apply(path, true, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Administrators")) | ||
acl.Apply(path, false, false, acl.GrantName(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, "Users")) |
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.
💡 Codebase verification
Based on the context gathered, I can now provide a final response:
Security concern confirmed: PublicWritePermission requires restrictive implementation
The PublicWritePermission
is used for temporary execution directories ("exec") and log directories that specifically require write access. However, granting GENERIC_ALL|STANDARD_RIGHTS_ALL
is still overly permissive as:
- The code comments acknowledge the permissions "may seem dangerous"
- It's used only for temporary execution directories that get cleaned up (
cleanAndEnsureExecDir()
) - The Unix equivalent is 0o777/0o666, suggesting only read/write/execute is needed
Recommendations:
- Replace
GENERIC_ALL|STANDARD_RIGHTS_ALL
with the minimum required permissions:GENERIC_READ|GENERIC_WRITE|GENERIC_EXECUTE
- Consider implementing separate permission levels for logs (which may need only append rights) versus exec directories
🔗 Analysis chain
Security concern: Review PublicWritePermission implementation
Granting GENERIC_ALL|STANDARD_RIGHTS_ALL
to the Users group gives excessive permissions. Consider restricting to specific required permissions instead of full control.
Let me gather more context about how this permission is used in the codebase.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for usage of PublicWritePermission to assess impact
rg "PublicWritePermission" --type go
Length of output: 546
Script:
#!/bin/bash
# Check the implementation and usage context of PublicWritePermission
ast-grep --pattern 'const PublicWritePermission = $_'
# Check the content of files using this permission
rg -B 5 -A 5 "PublicWritePermission" --type go
# Check what directories are created with these permissions
fd exec logs -t d -x ls -la {}
Length of output: 4702
base/utils/fs.go
Outdated
// Ignore windows permission error. For none admin users it will always fail. | ||
acl.Chmod(path, perm) | ||
SetDirPermission(path, perm) | ||
return nil |
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.
🛠️ Refactor suggestion
Add logging for Windows permission failures
Instead of silently ignoring Windows permission errors, log them to help diagnose permission issues during deployment and maintenance.
if isWindows {
// Ignore windows permission error. For none admin users it will always fail.
- SetDirPermission(path, perm)
+ if err := SetDirPermission(path, perm); err != nil {
+ log.Warningf("Failed to set directory permissions on Windows: %v", err)
+ }
return nil
}
Committable suggestion skipped: line range outside the PR's diff.
base/utils/structure.go
Outdated
// AsUnixDirPermission return the corresponding unix permission for a directory or executable. | ||
func (perm FSPermission) AsUnixDirExecPermission() fs.FileMode { | ||
switch perm { | ||
case AdminOnlyPermission: | ||
return 0o700 | ||
case PublicReadPermission: | ||
return 0o755 | ||
case PublicWritePermission: | ||
return 0o777 | ||
} | ||
|
||
return 0 | ||
} |
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.
Add validation for zero FSPermission value
Both permission conversion methods return 0 for invalid/zero FSPermission values without any error indication. This could lead to security issues if the permission value is not properly initialized.
Consider adding validation:
func (perm FSPermission) AsUnixDirExecPermission() fs.FileMode {
+ if perm > PublicWritePermission {
+ panic("invalid FSPermission value")
+ }
switch perm {
// ...
}
- return 0
}
func (perm FSPermission) AsUnixFilePermission() fs.FileMode {
+ if perm > PublicWritePermission {
+ panic("invalid FSPermission value")
+ }
switch perm {
// ...
}
- return 0
}
Also applies to: 33-45
base/utils/structure.go
Outdated
// AsUnixDirPermission return the corresponding unix permission for a regular file. | ||
func (perm FSPermission) AsUnixFilePermission() fs.FileMode { | ||
switch perm { | ||
case AdminOnlyPermission: | ||
return 0o600 | ||
case PublicReadPermission: | ||
return 0o655 | ||
case PublicWritePermission: | ||
return 0o666 | ||
} | ||
|
||
return 0 | ||
} |
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 incorrect file permission for PublicReadPermission
The file permission for PublicReadPermission
is set to 0o655
, which is unusual. The standard file permission for public-readable files is 0o644
(rw-r--r--).
Apply this fix:
case PublicReadPermission:
- return 0o655
+ return 0o644
📝 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.
// AsUnixDirPermission return the corresponding unix permission for a regular file. | |
func (perm FSPermission) AsUnixFilePermission() fs.FileMode { | |
switch perm { | |
case AdminOnlyPermission: | |
return 0o600 | |
case PublicReadPermission: | |
return 0o655 | |
case PublicWritePermission: | |
return 0o666 | |
} | |
return 0 | |
} | |
// AsUnixDirPermission return the corresponding unix permission for a regular file. | |
func (perm FSPermission) AsUnixFilePermission() fs.FileMode { | |
switch perm { | |
case AdminOnlyPermission: | |
return 0o600 | |
case PublicReadPermission: | |
return 0o644 | |
case PublicWritePermission: | |
return 0o666 | |
} | |
return 0 | |
} |
// TODO(vladimir): to set permissions on windows we need the full path of the file. | ||
err = t.Chmod(perm) |
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.
🛠️ Refactor suggestion
Consider implementing Windows-specific permission handling.
The TODO comment correctly identifies that Windows requires the full path for setting permissions. Consider implementing this using the Windows-specific APIs from the go-acl
package that's already imported.
Here's a suggested implementation:
-// TODO(vladimir): to set permissions on windows we need the full path of the file.
-err = t.Chmod(perm)
+if onWindows {
+ fullPath := t.Name()
+ err = acl.Chmod(fullPath, perm)
+} else {
+ err = t.Chmod(perm)
+}
Committable suggestion skipped: line range outside the PR's diff.
// TODO: distinguish between executable and non executable files. | ||
err = utils.SetExecPermission(rv.storagePath(), utils.PublicReadPermission) | ||
if err != nil { | ||
log.Warningf("%s: failed to set permissions on downloaded file %s: %s", reg.Name, rv.storagePath(), err) |
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.
💡 Codebase verification
Let me analyze the current findings and generate a final response based on the code search results.
Executable detection should be based on file extension and platform
The codebase already has platform-specific handling for executables, particularly for Windows (.exe
). The implementation should leverage this existing pattern:
base/utils/permissions.go
already has separate functions for executable and non-executable files (SetExecPermission
vsSetFilePermission
)cmds/portmaster-start/run.go
defines platform-specific executable suffixbase/profile/binmeta/name.go
identifies.exe
as Windows Executable
Suggested improvements:
- Use the existing
exeSuffix
constant fromportmaster-start/run.go
or create a shared constant - Check file extension against known executable extensions per platform
- For Windows: check
.exe
extension - For Unix: check file header (shebang) or executable bit of source file
🔗 Analysis chain
Consider implementing TODO for executable detection.
The current implementation sets execution permissions on all files, but the TODO suggests a need for differentiation. This could be important for security and functionality.
Consider implementing the following improvements:
- Add a flag in
ResourceVersion
to indicate if a file should be executable - Use file extension or resource type to automatically determine executability
- Add platform-specific logic in
utils.SetExecPermission
to handle this differentiation
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check current file types and their permissions
# Test: Look for patterns that could help identify executable files
# Check file extensions of resources
rg "NewResource\(" -A 3
# Check for existing executable detection logic
rg -g "*.go" "executable|\.exe"
Length of output: 37839
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.
Actionable comments posted: 0
🧹 Outside diff range and nitpick comments (6)
base/utils/structure.go (1)
11-17
: Consider adding documentation for FSPermission type and constantsThe new FSPermission type and its constants would benefit from detailed documentation explaining:
- The purpose and scope of each permission level
- Usage guidelines
- Platform-specific behavior (Windows vs Unix)
Add documentation like this:
+// FSPermission represents a cross-platform file system permission level type FSPermission uint8 const ( + // AdminOnlyPermission restricts access to administrative users only AdminOnlyPermission FSPermission = iota + // PublicReadPermission allows read access to all users PublicReadPermission + // PublicWritePermission allows read and write access to all users PublicWritePermission )base/utils/structure_test.go (1)
31-36
: Consider adding test cases for edge casesThe test could be enhanced by adding cases for:
- Invalid permission values
- Permission inheritance
- Permission changes after directory creation
cmds/updatemgr/release.go (3)
Line range hint
128-128
: Standardize file permissions using utils constantsFor consistency with the PR's objectives, replace the hardcoded
0o0644
with the appropriate utils permission constant.- err = os.WriteFile(path, jsonData, 0o0644) //nolint:gosec + err = os.WriteFile(path, jsonData, utils.PublicReadOnlyPermission)
Line range hint
40-44
: Add input validation for channel namesThe channel name is taken directly from command arguments without validation. Consider adding validation to prevent potential security issues.
func release(cmd *cobra.Command, args []string) error { channel := args[0] + // Validate channel name + if !isValidChannel(channel) { + return fmt.Errorf("invalid channel name: %s", channel) + }
Line range hint
143-157
: Add path validation for file deletionThe
removeFilesFromIndex
function deletes files without validating that the paths are within the expected directory structure.func removeFilesFromIndex(versions map[string]string) error { + baseDir := registry.StorageDir().Path // Print preview. fmt.Println("To be deleted:") for _, filePath := range versions { + // Ensure file is within the base directory + absPath, err := filepath.Abs(filePath) + if err != nil { + return fmt.Errorf("failed to resolve path: %w", err) + } + if !strings.HasPrefix(absPath, baseDir) { + return fmt.Errorf("file path outside base directory: %s", filePath) + } fmt.Println(filePath) }cmds/updatemgr/main.go (1)
34-34
: Verify error handling for permission-related failuresThe current error handling only returns the error without specific permission-related context. Consider adding more descriptive error messages for permission-related failures.
- err = registry.Initialize(utils.NewDirStructure(absDistPath, utils.PublicReadPermission)) - if err != nil { - return err - } + err = registry.Initialize(utils.NewDirStructure(absDistPath, utils.PublicReadPermission)) + if err != nil { + return fmt.Errorf("failed to initialize registry with permissions: %w", err) + }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (11)
base/database/storage/fstree/fstree.go
(1 hunks)base/updater/registry.go
(1 hunks)base/updater/registry_test.go
(1 hunks)base/utils/fs.go
(3 hunks)base/utils/permissions_windows.go
(1 hunks)base/utils/structure.go
(2 hunks)base/utils/structure_test.go
(2 hunks)cmds/notifier/main.go
(1 hunks)cmds/updatemgr/main.go
(1 hunks)cmds/updatemgr/release.go
(2 hunks)service/updates/upgrader.go
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
- base/updater/registry.go
- base/database/storage/fstree/fstree.go
- service/updates/upgrader.go
- base/utils/permissions_windows.go
- base/utils/fs.go
🔇 Additional comments (12)
base/updater/registry_test.go (2)
23-23
: LGTM! Good improvement in permission handling consistency.
The change from hardcoded permission to utils.PublicWritePermission
constant aligns well with the codebase-wide initiative to standardize permission handling.
23-23
: Verify the permission constant value for test environment.
Since this is setting up a test environment with a temporary directory, let's verify that PublicWritePermission
provides appropriate access levels for testing purposes.
✅ Verification successful
Permission constant is correctly used for test environment
The PublicWritePermission
constant is appropriately used in this test context as:
- It's a defined permission level in the utils package, used consistently across test files
- For test environments using temporary directories, write permission is necessary to set up and tear down test resources
- The same permission level is used in similar test scenarios across the codebase, showing consistent test environment setup patterns
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the permission constant definition and its usage in tests
# Expected: Find the constant definition and other test usages to confirm consistency
# Find the constant definition
echo "Permission constant definition:"
rg -A 1 "PublicWritePermission.*="
# Find other test files using this permission
echo -e "\nOther test files using this permission:"
rg "PublicWritePermission.*test.*go"
Length of output: 243
Script:
#!/bin/bash
# Let's try a broader search to find the constant and its usage
echo "Searching for PublicWritePermission constant:"
rg "PublicWritePermission"
echo -e "\nSearching in utils package for permission-related constants:"
fd ".*\.go" | grep -i "utils" | xargs cat | grep -A 2 "Permission.*="
echo -e "\nChecking the specific test file content:"
cat base/updater/registry_test.go
Length of output: 1938
base/utils/structure.go (3)
53-53
: LGTM: DirStructure changes look good
The changes to use FSPermission instead of os.FileMode in DirStructure improve type safety and cross-platform compatibility.
Also applies to: 59-59, 68-68
19-31
:
Add validation for invalid FSPermission values
The method returns 0 for invalid FSPermission values without any error indication, which could lead to security issues.
Add validation:
func (perm FSPermission) AsUnixDirExecPermission() fs.FileMode {
+ if perm > PublicWritePermission {
+ panic("invalid FSPermission value")
+ }
switch perm {
case AdminOnlyPermission:
return 0o700
case PublicReadPermission:
return 0o755
case PublicWritePermission:
return 0o777
}
- return 0
+ panic("unreachable: all cases should be handled")
}
33-45
:
Add validation for invalid FSPermission values in AsUnixFilePermission
Similar to AsUnixDirExecPermission, this method needs validation for invalid permission values.
Add validation:
func (perm FSPermission) AsUnixFilePermission() fs.FileMode {
+ if perm > PublicWritePermission {
+ panic("invalid FSPermission value")
+ }
switch perm {
case AdminOnlyPermission:
return 0o600
case PublicReadPermission:
return 0o644
case PublicWritePermission:
return 0o666
}
- return 0
+ panic("unreachable: all cases should be handled")
}
base/utils/structure_test.go (1)
16-22
: Verify permission values in test output comments
The output comments show Unix-style permissions (755, 777) but the test is marked with //go:build !windows
. Consider:
- Adding a Windows-specific test file
- Clarifying in comments that these are Unix permission representations
✅ Verification successful
Let me check the content of these files to better understand the permission handling across the codebase.
Permission values in test output comments are correct for Unix systems
The permission values in the test output comments are correct and consistent with the codebase:
- The test file is explicitly marked for non-Windows systems with
//go:build !windows
- The permission values (755, 777, 700) are standard Unix permission representations that align with the constants used in the code:
- PublicReadPermission (755)
- PublicWritePermission (777)
- AdminOnlyPermission (700)
No Windows-specific test file is needed because:
- The test is already correctly excluded from Windows builds
- The codebase handles Windows permissions separately (as seen in fstree.go with
onWindows
constant)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check if there's a Windows-specific test file
fd -e go structure_test_windows.go
# Check for other test files that might need similar updates
rg -l "os\.FileMode.*0[0-7]{3}" -t go
Length of output: 153
Script:
#!/bin/bash
# Check the content of related files
echo "=== structure_test.go ==="
cat base/utils/structure_test.go
echo -e "\n=== writefile_test.go ==="
cat base/utils/renameio/writefile_test.go
echo -e "\n=== fstree.go ==="
cat base/database/storage/fstree/fstree.go
# Check for build constraints
echo -e "\n=== Build constraints ==="
rg "^//go:build" base/utils/structure_test.go base/utils/renameio/writefile_test.go base/database/storage/fstree/fstree.go
Length of output: 10692
cmds/updatemgr/release.go (2)
14-14
: LGTM: Import addition aligns with permission standardization
The addition of the utils import supports the transition from hardcoded permissions to standardized constants.
67-70
: Verify symlink security measures
While using utils.PublicReadPermission
improves code maintainability, please ensure:
- The symlink target paths are properly validated
- The public read permissions are appropriate for the symlink directory
- The
registry.CreateSymlinks
function has proper security measures against symlink attacks
Let's verify the symlink handling implementation:
✅ Verification successful
Let me gather more information about the symlink creation implementation.
Let me check one more aspect about the permissions and directory structure.
Based on the gathered information, I can now provide a final response:
Symlink security measures are properly implemented
The implementation includes several security measures:
- The symlink root directory is completely wiped and recreated before new symlinks are created
- Target paths are confined within the storage directory using relative paths
- Proper permission handling is implemented with
utils.PublicReadPermission
(0o755 for directories, 0o644 for files) - The symlink creation uses relative paths between target and link, preventing path traversal
- The storage directory structure is managed through a dedicated
DirStructure
type that enforces permissions
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check symlink validation in CreateSymlinks
ast-grep --pattern 'func CreateSymlinks($_) {
$$$
}'
# Look for path validation patterns
rg -A 5 'filepath\.Clean|filepath\.Abs|filepath\.Rel'
Length of output: 6161
Script:
#!/bin/bash
# Check CreateSymlinks implementation
rg -A 20 "func.*CreateSymlinks"
# Check for symlink-related security measures
rg -A 10 "os\.Symlink|filepath\.EvalSymlinks"
# Look for permission constants
rg "PublicReadPermission.*=.*"
Length of output: 5574
Script:
#!/bin/bash
# Check for permission constants and directory structure
rg -A 5 "StorageDir|PublicReadPermission" base/
# Check resource validation
rg -A 10 "func.*storagePath" base/updater/
Length of output: 4435
cmds/updatemgr/main.go (2)
34-34
: Verify Windows compatibility of utils.PublicReadPermission
While replacing hardcoded permissions with constants improves maintainability, we should ensure the new permission constant works correctly on Windows systems.
Let's verify the implementation of PublicReadPermission
and its Windows-specific handling:
✅ Verification successful
Windows compatibility of utils.PublicReadPermission
is properly implemented
The constant is well-implemented with proper Windows-specific handling:
- In Windows (
permissions_windows.go
), it correctly sets:- Full access rights for Administrators and SYSTEM
- Read and Execute permissions for Users group
- On Unix systems, it maps to the equivalent
0o755
permission
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the implementation of PublicReadPermission and its usage
# Expected: Find Windows-specific permission handling in utils package
# Check the implementation of PublicReadPermission
ast-grep --pattern 'PublicReadPermission = $_'
# Look for Windows-specific permission handling
rg -p "windows" -B 5 -A 5 "PublicReadPermission"
# Check for any Windows-specific permission tests
rg -p "windows.*test" -B 5 -A 5 "PublicReadPermission"
Length of output: 272
Script:
#!/bin/bash
# Let's try a different approach to find the permission constant and its usage
# Find files containing PublicReadPermission
rg -l "PublicReadPermission"
# Look for permission-related constants in utils package
fd ".*\.go" utils/ --exec grep -l "Permission"
# Check for Windows-specific code in utils package
rg -p "windows" utils/
Length of output: 571
Script:
#!/bin/bash
# Let's examine the Windows-specific permission handling and the constant definition
# Check the Windows-specific permissions file
cat base/utils/permissions_windows.go
# Check the implementation of PublicReadPermission in structure.go
cat base/utils/structure.go
# Look for any permission-related tests
cat base/utils/structure_test.go
Length of output: 7281
34-34
: Verify permission consistency across the codebase
The AI summary mentions similar changes in other files. Let's ensure this change maintains consistency with the broader permission management approach.
✅ Verification successful
Based on the search results, I can now provide a definitive response about the permission consistency:
Permission usage is consistent with the codebase's approach
The change to use utils.PublicReadPermission
with NewDirStructure
is consistent with the broader permission management approach in the codebase. This is evidenced by:
- Similar usage in other initialization paths:
dataroot.Initialize(dataDir, utils.PublicReadPermission)
in multiple commandsregistry.Initialize(dataRoot.ChildDir("updates", utils.PublicReadPermission))
in update-related code- Other directory structures using the same permission constant
The permission system is well-defined in the codebase with clear hierarchical levels:
AdminOnlyPermission
PublicReadPermission
PublicWritePermission
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify consistent usage of permission constants
# Expected: Find similar patterns of permission constant usage
# Check for any remaining hardcoded 0755 permissions
rg -p "0[0-7]{3,4}" --type go
# Check the pattern of utils.NewDirStructure usage
ast-grep --pattern 'utils.NewDirStructure($_, $_)'
Length of output: 92670
Script:
#!/bin/bash
# Let's search for NewDirStructure usage and permission constants more specifically
# Search for NewDirStructure usage
ast-grep --pattern 'utils.NewDirStructure'
# Search for permission constants in utils package
rg -p "PublicRead|Permission" --type go ./utils/
Length of output: 142
Script:
#!/bin/bash
# Let's try searching from the repository root with more context
# Search for NewDirStructure usage and related code
rg -p "NewDirStructure|PublicRead|Permission" --type go
# Also look for the directory structure type definition
ast-grep --pattern 'type DirStructure struct'
Length of output: 20893
cmds/notifier/main.go (2)
235-238
: Verify security implications of PublicReadPermission for updates directory
The updates directory might contain sensitive data or be a target for tampering. We should verify that utils.PublicReadPermission
provides adequate protection.
✅ Verification successful
Let me gather more information about the permission constants and their usage.
Let me check one final aspect about the utils.PublicReadPermission definition and the directory structure implementation.
Based on the gathered information, I can now provide a final response about the security implications of using PublicReadPermission for the updates directory.
PublicReadPermission (0o755) is appropriate for the updates directory
The updates directory permission setting is secure because:
- The permission translates to 0o755 (read/execute for all, write only for owner)
- It's consistently used across different components (notifier, portmaster-start, updates service) for the updates directory
- The codebase shows proper validation of binaries in the updates directory (service/process/profile.go checks for unexpected binaries)
- The directory is used for storing verified updates with read-only access, which aligns with the principle of least privilege
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the security requirements for the updates directory
# Check how the updates directory is used
rg -A 5 "updates.*directory|directory.*updates"
# Look for any security documentation about the updates directory
fd -t f "README" -X rg -i "update.*security|security.*update"
# Check for any existing permission-related issues
rg -i "permission.*update|update.*permission"
Length of output: 2979
Script:
#!/bin/bash
# Check the definition and usage of PublicReadPermission
ast-grep --pattern 'const PublicReadPermission = $_'
# Check for other permission constants and their usage patterns
rg -A 2 "Permission.*=.*0"
# Check the Initialize function to understand how permissions are applied
ast-grep --pattern 'func Initialize($_) {
$$$
}'
# Look for any file permission checks or validations
rg -A 3 "os\.Chmod|os\.Stat"
Length of output: 8782
Script:
#!/bin/bash
# Check the definition of PublicReadPermission and related permission types
rg -A 5 "type.*Permission|PublicReadPermission"
# Check DirStructure implementation and permission handling
ast-grep --pattern 'func ($_ *DirStructure) ChildDir($_, $_) *DirStructure {
$$$
}'
Length of output: 74922
228-231
: Verify security implications of PublicReadPermission for data root
The change from hardcoded permissions to utils.PublicReadPermission
aligns with the PR objective of fixing Windows permissions. However, we should verify that this permission level is appropriate for the data root directory.
✅ Verification successful
Let me gather more information about the permission constants and their usage.
Let me search specifically for the permission constants and dataroot implementation.
Let me search for the specific permission constants and their implementations.
Let me check one more thing about the permission constants and their usage.
Based on the gathered information, I can now provide a final response about the security implications of using PublicReadPermission.
Using PublicReadPermission is appropriate for the data root directory
The change from hardcoded permissions to utils.PublicReadPermission
is secure and appropriate for the data root directory because:
PublicReadPermission
is consistently used across the codebase for data directories that need to be readable by users- On Windows, it properly sets up ACLs with read/execute rights for users while maintaining full admin rights
- On Unix systems, it translates to 0755 for directories (same as the previous hardcoded value)
- The implementation includes platform-specific handling to ensure proper security on both Windows and Unix systems
The permission is used similarly in other critical components like:
- The updates registry
- The database initialization
- The configuration system
- The updater service
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check the definition and usage of PublicReadPermission
# Check the definition of PublicReadPermission
rg -A 2 "PublicReadPermission.*="
# Check if there are any platform-specific permission implementations
ast-grep --pattern 'if runtime.GOOS == "windows" {
$$$
}'
# Look for any security-related comments or documentation
rg -i "permission.*security|security.*permission"
Length of output: 2491
Script:
#!/bin/bash
# Check for permission constants in utils package
rg -A 3 "const.*Permission"
# Check how dataroot.Initialize is used across the codebase
ast-grep --pattern 'dataroot.Initialize($_, $_)'
# Check the implementation of dataroot.Initialize
ast-grep --pattern 'func Initialize($_, $_) {
$$$
}'
# Look for any documentation about data directory permissions
rg -i "data.*directory.*permission|permission.*data.*directory"
Length of output: 65772
Script:
#!/bin/bash
# Check for PublicReadPermission constant definition
rg -A 2 "PublicReadPermission.*=" base/utils/
# Look for dataroot package implementation
rg -A 5 "func Initialize" base/dataroot/
# Check for any Windows-specific permission handling
rg -A 3 "windows.*permission|permission.*windows" -i
Length of output: 69347
Script:
#!/bin/bash
# Look for permission constants in utils package
rg -A 2 "type.*Permission" base/utils/
rg -A 2 "const.*Permission.*=" base/utils/
# Check for Windows-specific permission handling in utils
rg -A 5 "func setWindowsFilePermissions" base/utils/
Length of output: 744
Script:
#!/bin/bash
# Check the FSPermission type and constants definition
rg -A 10 "type FSPermission" base/utils/
# Look for other usages of PublicReadPermission
rg -A 2 "PublicReadPermission" --type go
Length of output: 4549
b362b3a
to
05a5d5e
Compare
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.
Actionable comments posted: 1
🧹 Outside diff range and nitpick comments (1)
service/updates/upgrader.go (1)
356-365
: Consider extracting permission constant to improve maintainabilityThe
PublicReadPermission
is used in both Windows and Unix paths. Consider extracting it to a named constant at the package level to ensure consistency and make future permission changes easier.Apply this diff to improve maintainability:
const ( upgradedSuffix = "-upgraded" exeExt = ".exe" + // executablePermission defines the permission for upgraded binaries + executablePermission = utils.PublicReadPermission ) // In upgradeBinary function: if onWindows { - _ = utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission) + _ = utils.SetExecPermission(fileToUpgrade, executablePermission) } else { - perm := utils.PublicReadPermission + perm := executablePermission // ... rest of the code }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (11)
base/database/storage/fstree/fstree.go
(1 hunks)base/updater/registry.go
(1 hunks)base/updater/registry_test.go
(1 hunks)base/utils/fs.go
(3 hunks)base/utils/permissions_windows.go
(1 hunks)base/utils/structure.go
(2 hunks)base/utils/structure_test.go
(2 hunks)cmds/notifier/main.go
(1 hunks)cmds/updatemgr/main.go
(1 hunks)cmds/updatemgr/release.go
(2 hunks)service/updates/upgrader.go
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
- base/updater/registry.go
- cmds/updatemgr/release.go
- cmds/notifier/main.go
- cmds/updatemgr/main.go
- base/updater/registry_test.go
- base/database/storage/fstree/fstree.go
- base/utils/fs.go
- base/utils/structure_test.go
- base/utils/structure.go
- base/utils/permissions_windows.go
🔇 Additional comments (1)
service/updates/upgrader.go (1)
23-23
: LGTM: Import changes align with permission management refactoring
The addition of the utils package and removal of go-acl aligns with the PR's objective of standardizing permission handling across the codebase.
if err != nil { | ||
return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err) | ||
} | ||
_ = utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission) |
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.
Handle errors from SetExecPermission on Windows
The error from SetExecPermission
is being silently ignored on Windows. This could mask permission-related issues during the upgrade process.
Apply this diff to add error handling:
-_ = utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission)
+if err := utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission); err != nil {
+ return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err)
+}
📝 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.
_ = utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission) | |
if err := utils.SetExecPermission(fileToUpgrade, utils.PublicReadPermission); err != nil { | |
return fmt.Errorf("failed to set permissions on %s: %w", fileToUpgrade, err) | |
} |
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.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (1)
base/utils/permissions_windows.go
(1 hunks)
🔇 Additional comments (2)
base/utils/permissions_windows.go (2)
1-8
: LGTM: Build constraints and imports are appropriate
The Windows build constraint and necessary imports for ACL management are correctly configured.
44-77
:
Critical: Improve security in SetFilePermission
Several security concerns need to be addressed:
- No error handling for ACL operations
- No path validation
- Overly permissive permissions for PublicWritePermission
Consider this safer implementation:
-func SetFilePermission(path string, perm FSPermission) {
+func setFilePermission(path string, perm FSPermission) error {
+ // Validate path
+ cleanPath, err := filepath.Abs(filepath.Clean(path))
+ if err != nil {
+ return fmt.Errorf("invalid path: %w", err)
+ }
+
+ // Prepare ACL entries
+ var entries []acl.Entry
+
switch perm {
case AdminOnlyPermission:
- acl.Apply(
- path,
- true,
- false,
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
- )
+ entries = []acl.Entry{
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
+ }
case PublicReadPermission:
- acl.Apply(
- path,
- true,
- false,
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
- acl.GrantSid(windows.GENERIC_READ|windows.GENERIC_EXECUTE, usersSID),
- )
+ entries = []acl.Entry{
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
+ acl.GrantSid(windows.GENERIC_READ|windows.GENERIC_EXECUTE, usersSID),
+ }
case PublicWritePermission:
- acl.Apply(
- path,
- true,
- false,
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
- acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, usersSID),
- )
+ entries = []acl.Entry{
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, systemSID),
+ acl.GrantSid(windows.GENERIC_ALL|windows.STANDARD_RIGHTS_ALL, adminsSID),
+ // Restrict user permissions to minimum required
+ acl.GrantSid(windows.GENERIC_READ|windows.GENERIC_WRITE|windows.GENERIC_EXECUTE, usersSID),
+ }
}
+
+ // Apply ACL entries
+ if err := acl.Apply(cleanPath, true, false, entries...); err != nil {
+ return fmt.Errorf("failed to set permissions: %w", err)
+ }
+
+ return nil
}
Let's verify the usage of PublicWritePermission to ensure we're not breaking existing functionality:
#!/bin/bash
# Search for usage of PublicWritePermission
rg "PublicWritePermission" --type go -B 3 -A 3
// SetDirPermission sets the permission of a directory. | ||
func SetDirPermission(path string, perm FSPermission) error { | ||
SetFilePermission(path, perm) | ||
return nil | ||
} | ||
|
||
// SetExecPermission sets the permission of an executable file. | ||
func SetExecPermission(path string, perm FSPermission) error { | ||
SetFilePermission(path, perm) | ||
return nil | ||
} |
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.
🛠️ Refactor suggestion
Refactor duplicate functions
SetDirPermission
and SetExecPermission
are identical. Consider combining them or implementing specific behavior for each type.
Consider this implementation:
-func SetDirPermission(path string, perm FSPermission) error {
- SetFilePermission(path, perm)
- return nil
-}
-
-func SetExecPermission(path string, perm FSPermission) error {
- SetFilePermission(path, perm)
- return nil
-}
+// FileType represents the type of file being secured
+type FileType int
+
+const (
+ TypeDirectory FileType = iota
+ TypeExecutable
+ TypeRegularFile
+)
+
+func SetPermission(path string, perm FSPermission, fileType FileType) error {
+ if err := ensureInitialized(); err != nil {
+ return err
+ }
+
+ // Add file type specific handling if needed
+ switch fileType {
+ case TypeDirectory:
+ // Directory specific checks/handling
+ case TypeExecutable:
+ // Executable specific checks/handling
+ }
+
+ return setFilePermission(path, perm)
+}
Committable suggestion skipped: line range outside the PR's diff.
base/utils/permissions_windows.go
Outdated
var ( | ||
systemSID *windows.SID | ||
adminsSID *windows.SID | ||
usersSID *windows.SID | ||
) | ||
|
||
func init() { | ||
var err error | ||
systemSID, err = windows.StringToSid("S-1-5") // NT Authority / SYSTEM | ||
if err != nil { | ||
panic(err) | ||
} | ||
adminsSID, err = windows.StringToSid("S-1-5-32-544") // Administrators | ||
if err != nil { | ||
panic(err) | ||
} | ||
usersSID, err = windows.StringToSid("S-1-5-32-545") // Users | ||
if err != nil { | ||
panic(err) | ||
} | ||
} |
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.
Critical: Improve error handling and variable scope
Several improvements are needed in the initialization code:
- Replace panic with proper error handling
- Add validation for SID strings
- Make variables package-private since they're only used internally
Consider this safer implementation:
-var (
+var (
- systemSID *windows.SID
- adminsSID *windows.SID
- usersSID *windows.SID
+ systemSID *windows.SID
+ adminsSID *windows.SID
+ usersSID *windows.SID
+ initErr error
)
+// validateSID checks if the provided SID string is valid
+func validateSID(sidStr string) bool {
+ sid, err := windows.StringToSid(sidStr)
+ return err == nil && sid != nil
+}
func init() {
- var err error
+ // Validate SID strings first
+ if !validateSID("S-1-5") || !validateSID("S-1-5-32-544") || !validateSID("S-1-5-32-545") {
+ initErr = fmt.Errorf("invalid SID string")
+ return
+ }
systemSID, err = windows.StringToSid("S-1-5") // NT Authority / SYSTEM
if err != nil {
- panic(err)
+ initErr = fmt.Errorf("failed to initialize system SID: %w", err)
+ return
}
// ... similar changes for other SIDs
}
+// ensureInitialized checks if initialization was successful
+func ensureInitialized() error {
+ if initErr != nil {
+ return fmt.Errorf("windows permissions not properly initialized: %w", initErr)
+ }
+ return nil
+}
Committable suggestion skipped: line range outside the PR's diff.
Copilot
AI
left a comment
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.
Copilot reviewed 8 out of 23 changed files in this pull request and generated 1 suggestion.
Files not reviewed (15)
- base/utils/structure.go: Evaluated as low risk
- cmds/portmaster-start/dirs.go: Evaluated as low risk
- cmds/notifier/main.go: Evaluated as low risk
- cmds/portmaster-start/main.go: Evaluated as low risk
- service/netquery/database.go: Evaluated as low risk
- base/config/main.go: Evaluated as low risk
- base/dataroot/root.go: Evaluated as low risk
- base/updater/registry.go: Evaluated as low risk
- base/database/main.go: Evaluated as low risk
- service/profile/module.go: Evaluated as low risk
- cmds/updatemgr/release.go: Evaluated as low risk
- cmds/updatemgr/main.go: Evaluated as low risk
- base/updater/registry_test.go: Evaluated as low risk
- service/core/base/global.go: Evaluated as low risk
- base/utils/structure_test.go: Evaluated as low risk
log.Warningf("%s: failed to set permissions on downloaded file %s: %s", reg.Name, rv.storagePath(), err) | ||
} | ||
// TODO: distinguish between executable and non executable files. | ||
err = utils.SetExecPermission(rv.storagePath(), utils.PublicReadPermission) |
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.
The function SetExecPermission
is used for all files without distinguishing between executable and non-executable files. This could lead to incorrect permissions being set. Consider using different functions for setting permissions based on the file type.
err = utils.SetExecPermission(rv.storagePath(), utils.PublicReadPermission) | |
if isExecutable(rv.storagePath()) { err = utils.SetExecPermission(rv.storagePath(), utils.PublicReadPermission) } else { err = os.Chmod(rv.storagePath(), 0o0644) } |
Copilot is powered by AI, so mistakes are possible. Review output carefully before use.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation
Chores