Skip to content

PDF replacement can retain 0600 permissions after Media Library Replace file #1107

Description

@pirate-bot

Summary

Replacing an existing PDF through Optimole’s Media Library Replace file action can leave the replacement with 0600 filesystem permissions. The replacement is expected to retain web-readable file permissions comparable to a normal Media Library upload. In the reported case, the direct PDF URL instead returns 403 Forbidden until its permissions are manually changed to 0644, making the replaced document unavailable to visitors.

Customer context

Product / area: Optimole, Media Library attachment replacement
Version: Not provided
Environment: WordPress; hosting, web-server, PHP, and filesystem method details were not provided
Integration / third party: None identified
Reported error / symptom: A normally uploaded PDF is 0644 and accessible; replacing that PDF through Edit more details > Replace file leaves it at 0600 and its direct URL returns 403 Forbidden
Impact: The replaced PDF is inaccessible until its filesystem permissions are manually corrected

Reproduction notes

Reported reproduction:

  1. Upload a PDF normally through the WordPress Media Library and observe 0644; confirm its direct URL is accessible.
  2. Open the attachment via Media Library > Edit more details.
  3. Use Optimole’s Replace file action to upload another PDF.
  4. Observe that the replacement has 0600 permissions and its direct URL returns 403 Forbidden.
  5. Change the mode manually to 0644; the URL becomes accessible immediately.

The customer reports reproducing this with a separate test PDF. The workflow was not independently executed during repository inspection; hosting and version details are missing.

Diagnosis

Conclusion

The reported permission failure is consistent with a reachable Optimole replacement path. The handler moves the PHP upload temporary file directly over the existing attachment, attempts a filesystem permission normalization, and reports success without checking whether that operation succeeded. The customer’s controlled normal-upload versus replacement comparison provides runtime evidence that the replacement can remain at 0600. The precise host-level reason for the failed normalization is not established.

Where this likely occurs

  • Media Library attachment edit surface: inc/media_rename/attachment_edit.phpOptml_Attachment_Edit::replace_file() lines 332–377 validates the uploaded PDF and passes the raw $_FILES['file'] temporary path into the replacer.
  • Attachment replacement filesystem path: inc/media_rename/attachment_replace.phpOptml_Attachment_Replace::replace() lines 61–105 moves the temporary file onto the existing source path at lines 82–84, calls chmod( $original_file, FS_CHMOD_FILE ) at line 86, does not inspect its result, and returns success at line 105.
  • Git history: commit 6077f05c (fix: file permissions after move) introduced the permission call and is contained in tags v4.0.0 through v4.2.10. The replacement feature first shipped in the v4.0.0 version boundary.

Engineering notes

The move occurs before permission normalization, so the destination can initially inherit properties of the PHP upload temporary file. FS_CHMOD_FILE and the active WP_Filesystem implementation come from the WordPress runtime; WordPress core source was not present in the inspected plugin checkout, so behavior across filesystem transports was not independently verified. The current Optimole path treats replacement as successful even when permission normalization returns a failure. PDF files follow this same path and do not have an image-only branch around the permission call. The resulting HTTP 403 is produced by the hosting/web-server access layer after the replacement leaves the file unreadable to that layer.

Test coverage status

tests/media_rename/test-attachment-edit.phptest_replace_file_valid_jpeg_replacement() lines 256–287 covers a successful JPEG AJAX replacement but does not assert destination permissions and does not model the reported PDF case. tests/media_rename/test-attachment-replace.phptest_replacements() and do_replace_test() lines 65–139 cover JPEG replacement and metadata outcomes without permission assertions. No relevant PDF or 0600 permission coverage was found during inspection. Tests were inspected but not executed because the product workspace is read-only and the WordPress test environment was not established for this investigation.

What to verify or explore next

  • Reproduce with a PDF whose PHP upload temporary file begins at 0600, recording the destination mode before and after Optml_Attachment_Replace::replace().
  • Verify the return value of the filesystem permission operation and the runtime value of FS_CHMOD_FILE in the affected environment.
  • Compare direct-filesystem and other supported WP_Filesystem transports, including file ownership and process umask differences.
  • Run the focused media replacement PHPUnit suites in tests/media_rename/test-attachment-edit.php and tests/media_rename/test-attachment-replace.php under the affected WordPress/PHP environment.

Unknowns / follow-up

The affected Optimole, WordPress, and PHP versions are unknown. The active filesystem transport, destination ownership, process user, umask, and reason for the permission operation’s failure were not provided. No ticket image was available, and no matching existing issue was found in Codeinwp/optimole-wp.

Confidence

Confidence: 88/100

The customer supplied a controlled comparison showing that only Optimole’s replacement path leaves the PDF at 0600, and repository inspection confirms that this path moves the upload temporary file onto the attachment while treating an unchecked permission-normalization call as successful. The exact reason chmod fails in the reported environment remains unknown, but the product path can complete with an inaccessible replacement.


Source: HelpScout #3415371219
Generated by bug-report-triage (ID: bug-report-triage_6a7b390d21f812.49085803)

Metadata

Metadata

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions