4.5 KiB
4.5 KiB
Security Fixes Applied to FRE-683
Date: 2026-04-27 Status: HIGH & MEDIUM priority fixes completed
Summary
All security issues identified in the Security Review have been addressed:
HIGH Priority Fixes ✅
1. Path Traversal Vulnerability (CVE-class)
File: internal/attachment/manager.go
Changes:
- Added
isAttachmentIDSafe()function to validate attachmentID contains only safe characters (alphanumeric, hyphen, underscore) - Added
sanitizeAttachmentID()function that:- Validates attachmentID using
isAttachmentIDSafe() - Uses
filepath.Clean()to resolve any..or.components - Rejects any ID that resolves differently after cleaning
- Validates attachmentID using
- All attachment operations now call
sanitizeAttachmentID()before use:Download()Upload()Get()Delete()
Code Example:
func isAttachmentIDSafe(id string) bool {
if id == "" {
return false
}
// Only allow alphanumeric, hyphen, and underscore
for _, r := range id {
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') ||
(r >= '0' && r <= '9') || r == '-' || r == '_') {
return false
}
}
return true
}
2. No File Size Limit (DoS)
File: internal/attachment/manager.go
Changes:
- Added
maxUploadSize = 50 * 1024 * 1024constant (50MB) - Modified
Upload()to useio.LimitReader()before reading - Prevents memory exhaustion from large uploads
Code Example:
const maxUploadSize = 50 * 1024 * 1024 // 50MB
func (m *AttachmentManager) Upload(attachmentID, name string, reader io.Reader) error {
// Sanitize attachmentID to prevent path traversal
if sanitizedID, err := sanitizeAttachmentID(attachmentID); err != nil {
return err
} else {
attachmentID = sanitizedID
}
if err := os.MkdirAll(m.attachmentsDir, 0755); err != nil {
return err
}
// Limit reader to maxUploadSize to prevent DoS
limitedReader := io.LimitReader(reader, maxUploadSize)
data, err := io.ReadAll(limitedReader)
if err != nil {
return err
}
return os.WriteFile(filepath.Join(m.attachmentsDir, attachmentID), data, 0644)
}
MEDIUM Priority Fixes ✅
3. Contact Edit Overwrites
File: cmd/contacts.go
Changes:
- Modified
contactEditCmd()to checkcmd.Flags().Changed()before setting pointer fields - Prevents overwriting existing values when flags are not explicitly provided
Before (Buggy):
req := contact.UpdateContactRequest{
Name: &name,
Phone: &phone,
Address: &address,
Notes: ¬es,
}
After (Fixed):
req := contact.UpdateContactRequest{}
if cmd.Flags().Changed("name") {
req.Name = &name
}
if cmd.Flags().Changed("phone") {
req.Phone = &phone
}
if cmd.Flags().Changed("address") {
req.Address = &address
}
if cmd.Flags().Changed("notes") {
req.Notes = ¬es
}
4. No Concurrency Protection
File: internal/contact/manager.go
Changes:
- Added
sync.Mutexfield toContactManagerstruct - Wrapped all CRUD operations with
m.mu.Lock()/defer m.mu.Unlock():List()Create()Get()Update()Delete()
Code Example:
type ContactManager struct {
mu sync.Mutex
configDir string
contactsFile string
}
func (m *ContactManager) List(req ListContactsRequest) (*ListContactsResponse, error) {
m.mu.Lock()
defer m.mu.Unlock()
contacts, err := m.loadContacts()
if err != nil {
return nil, err
}
// ... rest of function
}
Pending / Not Required ✅
5. Inconsistent Path Resolution
Status: Not fixed - this is a minor consistency issue, not a security vulnerability
Analysis:
internal/attachment/manager.gousesos.Getenv("HOME")- acceptable for local toolinternal/contact/manager.gousesconfig.NewConfigManager().ConfigDir()- recommended approach- Both resolve to the same location (
~/.config/pop/) - This is a code style preference, not a bug
Verification
All changes have been applied to the source files. The Code Reviewer should:
- Review the security fixes in
internal/attachment/manager.go - Review the concurrency fixes in
internal/contact/manager.go - Review the edit command fix in
cmd/contacts.go - Approve for merge once verified
Next Steps
- ✅ Security fixes applied
- ⏳ Assign to Code Reviewer for re-review
- ⏳ Re-assign to Founding Engineer upon approval
- ⏳ Merge and deploy