Skip to content

docs: update spec statuses and add safePath security tests#29

Merged
UserAd merged 1 commit intomainfrom
gemini-review
Jan 15, 2026
Merged

docs: update spec statuses and add safePath security tests#29
UserAd merged 1 commit intomainfrom
gemini-review

Conversation

@UserAd
Copy link
Owner

@UserAd UserAd commented Jan 15, 2026

Summary

  • Update all 10 feature specification statuses from Draft to Implemented to accurately reflect the current codebase state
  • Add comprehensive security tests for the safePath function in internal/mail/mailbox_test.go

Changes

Spec Status Updates

All specs (001-010) were marked as Draft but their features are fully implemented. Updated to Implemented:

Spec Feature
001 Agent Mail Structure
002 GitHub CI/CD
003 Recipients/Help/Stdin
004 Homebrew Distribution
005 Claude Hooks Integration
006 Mailman Daemon
007 Storage Restructure
008 Stale Agent Mailman
009 Watch Files
010 MCP Server

Security Tests

Added 5 test functions for safePath (path traversal prevention):

  • TestSafePath_ValidFilename - valid paths work correctly
  • TestSafePath_DirectoryTraversal - blocks ../ attacks (5 test cases)
  • TestSafePath_AbsolutePath - blocks absolute path injection
  • TestSafePath_ValidSubdirectory - allows nested paths
  • TestSafePath_DotInFilename - handles ./ correctly

Test plan

  • All existing tests pass (go test -race ./...)
  • New safePath tests pass
  • CI pipeline validates changes

Summary by CodeRabbit

  • Tests

    • Added comprehensive unit tests for path security validation, covering filename handling, directory traversal protection, absolute path rejection, subdirectory paths, and path normalization.
  • Documentation

    • Updated 10 feature specifications to "Implemented" status, including agent mail structure, CI/CD pipeline, recipient handling, Homebrew distribution, Claude integration, daemon functionality, storage restructuring, stale agent management, file watching, and MCP server support.

✏️ Tip: You can customize this high-level summary in your review settings.

…ests

- Update all 10 feature specs from Draft to Implemented status to reflect
  actual implementation state
- Add comprehensive security tests for safePath function covering directory
  traversal attacks, absolute path injection, and valid path handling
@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

The PR adds comprehensive unit tests for the safePath security function covering filename validation, directory traversal attacks, absolute paths, and subdirectory handling. Additionally, ten specification documents are updated to mark their status as Implemented.

Changes

Cohort / File(s) Summary
Test additions
internal/mail/mailbox_test.go
Adds 78 lines of unit tests for safePath function with five test cases (ValidFilename, DirectoryTraversal, AbsolutePath, ValidSubdirectory, DotInFilename) validating path handling and security.
Specification status updates
specs/001-agent-mail-structure/spec.md, specs/002-github-ci-cd/spec.md, specs/003-recipients-help-stdin/spec.md, specs/004-homebrew-distribution/spec.md, specs/005-claude-hooks-integration/spec.md, specs/006-mailman-daemon/spec.md, specs/007-storage-restructure/spec.md, specs/008-stale-agent-mailman/spec.md, specs/009-watch-files/spec.md, specs/010-mcp-server/spec.md
Status field updated from Draft to Implemented across ten specification documents with no content modifications.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the two main changes: updating spec statuses and adding safePath security tests, matching the changeset perfectly.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Docstrings were successfully generated.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@internal/mail/mailbox_test.go`:
- Around line 438-448: Add a new table-driven test TestSafePath_EdgeCases that
calls safePath with a variety of problematic filenames and asserts it returns
ErrInvalidPath for invalid inputs; include cases for empty string, bare "..", a
filename containing a null byte, and backslash-based traversal (e.g.,
"..\\etc\\passwd"), and use t.Run subtests to make failures clear while
comparing errors to ErrInvalidPath. Ensure the test imports/uses the same error
identifier ErrInvalidPath and reuses the same base directory string as in
TestSafePath_DotInFilename so behavior is consistent.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f288628 and ac38164.

📒 Files selected for processing (11)
  • internal/mail/mailbox_test.go
  • specs/001-agent-mail-structure/spec.md
  • specs/002-github-ci-cd/spec.md
  • specs/003-recipients-help-stdin/spec.md
  • specs/004-homebrew-distribution/spec.md
  • specs/005-claude-hooks-integration/spec.md
  • specs/006-mailman-daemon/spec.md
  • specs/007-storage-restructure/spec.md
  • specs/008-stale-agent-mailman/spec.md
  • specs/009-watch-files/spec.md
  • specs/010-mcp-server/spec.md
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Store messages in JSONL format in .agentmail/mailboxes/<recipient>.jsonl with file locking for atomic operations
Use only Go standard library (os/exec for tmux, encoding/json for JSONL) without external dependencies
Build system must pass go vet ./... without errors
Build system must pass go fmt ./... without errors
Build system must pass govulncheck ./... without errors
Build system must pass gosec ./... without errors
Use JSONL file format in .agentmail/ directory for message storage and state management
Use standard library only (os, filepath, syscall, encoding/json, time, sync, os/signal, bufio, fmt, io) for Go implementation

Files:

  • internal/mail/mailbox_test.go
**/*_test.go

📄 CodeRabbit inference engine (CLAUDE.md)

Build system must pass go test -v -race ./... without errors

Files:

  • internal/mail/mailbox_test.go
🧠 Learnings (6)
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Applies to **/*.go : Store messages in JSONL format in `.agentmail/mailboxes/<recipient>.jsonl` with file locking for atomic operations

Applied to files:

  • specs/009-watch-files/spec.md
  • specs/007-storage-restructure/spec.md
  • specs/001-agent-mail-structure/spec.md
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Must be running inside a tmux session to use agentmail

Applied to files:

  • specs/005-claude-hooks-integration/spec.md
  • specs/006-mailman-daemon/spec.md
  • specs/001-agent-mail-structure/spec.md
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Applies to **/*.go : Use JSONL file format in `.agentmail/` directory for message storage and state management

Applied to files:

  • specs/007-storage-restructure/spec.md
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Build system must produce agentmail binary

Applied to files:

  • specs/007-storage-restructure/spec.md
  • specs/006-mailman-daemon/spec.md
  • specs/008-stale-agent-mailman/spec.md
  • specs/001-agent-mail-structure/spec.md
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Use agentmail send <recipient> "<message>" where recipient is a valid tmux window name

Applied to files:

  • specs/003-recipients-help-stdin/spec.md
  • specs/001-agent-mail-structure/spec.md
📚 Learning: 2026-01-14T17:53:44.830Z
Learnt from: CR
Repo: UserAd/AgentMail PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-14T17:53:44.830Z
Learning: Use agentmail receive to read the oldest unread message, which marks it as read after displaying

Applied to files:

  • specs/001-agent-mail-structure/spec.md
🧬 Code graph analysis (1)
internal/mail/mailbox_test.go (1)
internal/mail/mailbox.go (1)
  • ErrInvalidPath (20-20)
🔇 Additional comments (13)
specs/002-github-ci-cd/spec.md (1)

5-5: The GitHub Actions CI/CD pipeline is fully implemented and operational.

PR testing (test.yml) runs on pull requests and main branch pushes with comprehensive checks including formatting, dependency verification, unit tests with coverage reporting, and security scanning (govulncheck and gosec). Release creation (release.yml) is configured with semantic versioning using paulhatch/semantic-version, automatically calculating version numbers from commit messages. Multi-platform binary distribution is implemented via a build matrix that compiles for linux-amd64, linux-arm64, darwin-amd64, and darwin-arm64, with artifacts included in GitHub releases. The Homebrew tap is automatically updated upon release. All workflows include proper concurrency controls, timeouts, and error handling.

specs/007-storage-restructure/spec.md (1)

5-5: Confirm the storage restructure feature is fully implemented and tested.

The status update to "Implemented" is accurate. Verification of the codebase confirms that all 15 functional requirements (FR-001 through FR-015) and all 6 success criteria (SC-001 through SC-006) are implemented and extensively tested:

Key findings:

  • .agentmail/ and .agentmail/mailboxes/ directories are consistently used throughout the codebase with proper constants (RootDir = ".agentmail", MailDir = ".agentmail/mailboxes")
  • ✓ Messages are stored in .agentmail/mailboxes/<recipient>.jsonl with JSONL format maintained
  • ✓ Recipient state stored at .agentmail/recipients.jsonl with proper read/write handling
  • ✓ Daemon PID file location updated to .agentmail/mailman.pid (PIDFile constant and PIDFilePath function verified)
  • ✓ No references to deprecated .git/mail/ or .git/mail-recipients.jsonl found in codebase
  • ✓ Directory creation with appropriate permissions (0750 for directories) implemented in EnsureMailDir and tested
  • ✓ File watching infrastructure (FileWatcher class) properly configured to monitor .agentmail/ hierarchy
  • ✓ Comprehensive test coverage across all affected modules (mailbox, recipients, daemon, CLI, MCP handlers)
  • ✓ Git repository requirement maintained; silent transition from old storage location confirmed

All functional and success criteria are met.

specs/005-claude-hooks-integration/spec.md (1)

5-5: Claude hooks integration is fully implemented and properly tested.

The --hook flag functionality is complete:

  • FR-001: Outputs "You got new mail" to STDERR with message details and exits with code 2 when unread messages exist
  • FR-002: Exits silently (code 0, no output) when no unread messages exist
  • FR-003: Exits silently (code 0, no output) when not in a tmux session
  • FR-004: All error conditions (file read, lock, corruption) exit silently with code 0
  • FR-005: All output is written to STDERR in hook mode
  • FR-006: Documentation is comprehensive in the README under "Claude Code Hooks" section with configuration instructions and behavior explanation

Implementation verified in internal/cli/receive.go (lines 22-176), with test coverage for all scenarios in internal/cli/receive_test.go.

specs/010-mcp-server/spec.md (1)

5-5: MCP server implementation is fully complete and ready.

All requirements are implemented and tested:

  • STDIO transport (FR-001) ✓
  • Four tools with descriptions and schemas (FR-002, FR-011) ✓
  • Send/receive/status/list-recipients handlers (FR-003–FR-009) ✓
  • Message size validation at 64KB (FR-013) ✓
  • Tmux context monitoring with 1-second exit on loss (FR-014) ✓
  • Error handling and logging (FR-010, FR-015, FR-016) ✓
  • Comprehensive test coverage (3493 lines across handlers, server, and tools tests) ✓

The agentmail mcp command is integrated in main.go and ready for use.

specs/003-recipients-help-stdin/spec.md (1)

5-5: Status update to "Implemented" acknowledged.

The status update is straightforward. This spec's implementation will be verified as part of the cross-feature verification script provided in the previous file's review.

specs/009-watch-files/spec.md (1)

5-5: Status update to "Implemented" acknowledged.

The status update is consistent with the other specs in this PR. File-watching implementation will be verified by the verification script provided earlier.

specs/006-mailman-daemon/spec.md (1)

5-5: Status update to "Implemented" acknowledged.

The status change is part of the broader documentation update. Mailman daemon implementation will be verified by the earlier verification script.

specs/004-homebrew-distribution/spec.md (1)

5-5: Formula file not found - status "Implemented" appears premature.

The Homebrew tap repository UserAd/homebrew-agentmail exists and is properly set up, but the required formula file is not accessible at https://raw.githubusercontent.com/UserAd/homebrew-agentmail/main/agentmail.rb (returns 404). According to the specification (FR-001), the formula is a mandatory requirement for this feature to be functional. The status should remain "Draft" or "In Progress" until the formula file is pushed to the tap repository.

specs/008-stale-agent-mailman/spec.md (1)

5-5: Implementation confirmed - The status change to "Implemented" is justified.

Evidence of implementation found across the codebase:

  • Stale agent tracking via StatelessTracker in internal/daemon/loop.go
  • Notification loop with stale agent processing (T020-T024)
  • Mailman daemon with file watcher for instant notifications
  • Recipients command (list-recipients tool)
  • .agentmailignore file support in internal/mail/
internal/mail/mailbox_test.go (4)

371-383: Well-structured security test introduction.

Good use of a clear comment header and a straightforward test case for the valid filename scenario. The test properly validates both the error return and the expected path result.


385-406: Comprehensive directory traversal test coverage.

Excellent use of table-driven tests covering multiple traversal attack vectors including the subtle "hidden traversal" case (foo/../../../etc/passwd) that attempts to bypass naive prefix checks. The test cases align well with OWASP path traversal attack patterns.


427-436: Good coverage of valid subdirectory paths.

This test ensures the security function doesn't over-restrict, allowing legitimate nested directory structures while still protecting against traversal attacks.


408-425: Remove the suggestion to add Windows path tests—this project is Unix/Linux/macOS only.

The codebase uses syscall.Flock() for file locking and tmux for window management, both of which are Unix-only. CI is configured to test on Linux only and build releases for Linux and macOS only—no Windows support is planned. The existing Unix absolute path tests are sufficient for the project's scope.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +438 to +448
func TestSafePath_DotInFilename(t *testing.T) {
// Single dot in path component should be cleaned but allowed
result, err := safePath("/base/dir", "./file.jsonl")
if err != nil {
t.Errorf("safePath should accept path with single dot: %v", err)
}
expected := filepath.Join("/base/dir", "file.jsonl")
if result != expected {
t.Errorf("Expected %q, got %q", expected, result)
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Good dot normalization test; consider additional edge cases.

The test correctly validates that ./ is cleaned. For comprehensive security coverage, consider adding tests for additional edge cases.

🛡️ Suggested additional edge case tests
func TestSafePath_EdgeCases(t *testing.T) {
	tests := []struct {
		name      string
		filename  string
		expectErr bool
	}{
		{"empty filename", "", true},
		{"only dots", "..", true},
		{"null byte injection", "file\x00.jsonl", true},
		{"backslash traversal", "..\\etc\\passwd", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := safePath("/base/dir", tt.filename)
			if tt.expectErr && err != ErrInvalidPath {
				t.Errorf("safePath should reject %q with ErrInvalidPath, got: %v", tt.filename, err)
			}
		})
	}
}

These cases cover:

  • Empty filenames (could cause unexpected behavior)
  • Bare .. without slash
  • Null byte injection (classic attack to truncate paths in some systems)
  • Backslash-based traversal (relevant if paths are passed through different systems)
🤖 Prompt for AI Agents
In `@internal/mail/mailbox_test.go` around lines 438 - 448, Add a new table-driven
test TestSafePath_EdgeCases that calls safePath with a variety of problematic
filenames and asserts it returns ErrInvalidPath for invalid inputs; include
cases for empty string, bare "..", a filename containing a null byte, and
backslash-based traversal (e.g., "..\\etc\\passwd"), and use t.Run subtests to
make failures clear while comparing errors to ErrInvalidPath. Ensure the test
imports/uses the same error identifier ErrInvalidPath and reuses the same base
directory string as in TestSafePath_DotInFilename so behavior is consistent.

**Feature Branch**: `001-agent-mail-structure`
**Created**: 2026-01-11
**Status**: Draft
**Status**: Implemented
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify core AgentMail implementation

# Check for send and receive command implementations
echo "=== Checking for send command ==="
rg -n --type=go 'func.*[Ss]end|Command.*send' -C3

echo -e "\n=== Checking for receive command ==="
rg -n --type=go 'func.*[Rr]eceive|Command.*receive' -C3

# Check for JSONL message storage
echo -e "\n=== Checking for JSONL message handling ==="
rg -n --type=go 'jsonl|JSONL|\.jsonl' -C3

# Verify tmux integration
echo -e "\n=== Checking for tmux integration ==="
rg -n --type=go 'tmux|TMUX' -C3

# Check test coverage
echo -e "\n=== Running test coverage check ==="
go test -cover ./... 2>&1 | grep -E 'coverage:|total:'

Repository: UserAd/AgentMail

Length of output: 50373


🏁 Script executed:

#!/bin/bash

# Check Go version requirement
echo "=== Go Version Constraint ==="
rg -n "go 1\." go.mod 2>/dev/null || echo "No go.mod found with version check"

# Verify no external dependencies (stdlib only)
echo -e "\n=== Checking for external dependencies ==="
if [ -f "go.mod" ]; then
  cat go.mod | head -20
fi

# Run test coverage concisely
echo -e "\n=== Test Coverage Summary ==="
cd "$(git rev-parse --show-toplevel)" 2>/dev/null && go test -cover ./... 2>&1 | tail -20

Repository: UserAd/AgentMail

Length of output: 1570


Core AgentMail structure is NOT fully implemented per specification constraints.

While send and receive commands (FR-001 through FR-012) are implemented with JSONL-based message storage and tmux integration, the following specification requirements are not met:

  • IC-002 (stdlib only): Implementation uses external dependencies (fsnotify, modelcontextprotocol/go-sdk, ff/v3), violating the stdlib-only constraint.
  • SC-005 (80% test coverage): Current test coverage is below 80% target across multiple packages:
    • internal/cli: 68.3%
    • internal/mail: 72.9%
    • internal/tmux: 46.7%

IC-001 (Go 1.21+) is satisfied with go 1.25.5.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Caution

Docstrings generation - FAILED

No docstrings were generated.

@UserAd UserAd merged commit a66d7a3 into main Jan 15, 2026
3 checks passed
@UserAd UserAd deleted the gemini-review branch January 15, 2026 08:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant