Skip to content

fix(penpal): address Codex security findings from PR #297#302

Merged
loganj merged 1 commit intomainfrom
fix/penpal-security-hardening
Feb 26, 2026
Merged

fix(penpal): address Codex security findings from PR #297#302
loganj merged 1 commit intomainfrom
fix/penpal-security-hardening

Conversation

@loganj
Copy link
Collaborator

@loganj loganj commented Feb 26, 2026

Summary

Addresses 3 P1 security issues flagged by Codex on #297 (plus 2 additional instances of the same pattern):

  • Bind HTTP server to loopback only — changed ":%d" to "127.0.0.1:%d" so penpal only listens on localhost
  • Path traversal protection — added isSubpath() helper and applied it to all 5 endpoints that join untrusted input with project paths: handleRawFile, handlePublish, clipboard copy, file delete, and add source
  • Comment sidecar path validationcommentsPath() now resolves and validates the full path stays within .penpal/comments/

Closes PENPAL-1

Test plan

  • go test ./... passes in apps/penpal
  • New TestIsSubpath covers valid subpaths, sibling paths, and .. traversal
  • Manual: curl localhost:8082/api/raw?project=...&path=../../etc/passwd returns 400

🤖 Generated with Claude Code

- Bind HTTP server to 127.0.0.1 instead of all interfaces
- Add path traversal protection to all endpoints that join untrusted
  input with project paths (handleRawFile, handlePublish, clipboard copy,
  file delete, add source)
- Validate comment sidecar paths stay within the .penpal/comments directory
- Add isSubpath helper with tests

Closes PENPAL-1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@loganj loganj marked this pull request as ready for review February 26, 2026 20:29
@loganj loganj added the ai-outer-loop Managed by outer-loop label Feb 26, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5311fca680

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

// Verify the file exists and is under the project directory
if !strings.HasPrefix(fullPath, project.Path+"/") {
resolved, err := filepath.Abs(fullPath)
if err != nil || !isSubpath(project.Path, resolved) {

Choose a reason for hiding this comment

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

P2 Badge Reject project-root path in delete handler

isSubpath(project.Path, resolved) treats resolved == project.Path as valid, so a request with path=. now passes validation and reaches os.Remove(resolved). Unlike the previous project.Path + "/" prefix check, this allows deleting the project directory itself when it is empty (or returning a confusing 500 when it is not), which is a data-loss regression for crafted API calls against empty projects.

Useful? React with 👍 / 👎.

@loganj loganj merged commit 8511149 into main Feb 26, 2026
3 checks passed
@loganj loganj deleted the fix/penpal-security-hardening branch February 26, 2026 20:33
loganj added a commit that referenced this pull request Feb 26, 2026
isSubpath(parent, child) previously returned true when child == parent,
allowing path=. to pass validation and reach os.Remove in the delete
handler. This could delete the project directory itself on empty projects.

Make the check strictly "inside parent" so equal paths are rejected.

Addresses Codex review comment on PR #302.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-outer-loop Managed by outer-loop

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant