4 Commits
Author SHA1 Message Date
Henrik RydgårdandClaude Opus 5.5 abeb890d0f Install savedata zips that contain a single save folder's files in the root
Some shared saves are zipped without their folder, with the zip named after it
instead (ULUS10019000.zip). Detect these by a root PARAM.SFO with a
SAVEDATA_DIRECTORY, which holds the folder name (game ID + save name), and
extract into that folder.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 12:27:24 +02:00
Henrik RydgårdandClaude Opus 5.5 1b1e3aba1f Fix installing save state zips
Save state zips are detected with stripChars past the last slash, since
the whole directory is stripped. ExtractZipContents then computed a
negative directory length, created a directory named after each file,
failed to open it for writing and reported "Storage full".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-11 18:27:30 +02:00
499cdd55ad Validate untrusted IDs before using them as directory names
Texture pack installs took the destination directory from the archive's
textures.ini, and PKG update installs from the PBOOT's DISC_ID, both
unchecked, so a crafted archive or package could write outside
PSP/TEXTURES or PSP/GAME. Add IsSafePathComponent() to PathUtil and check
both, replacing the WebServer's own upload filename check with it too.

DISC_ID from any PARAM.SFO also names configs, cheats, textures, exdata
and savestates, so ReadSFO now drops one that isn't a safe name and the
game gets a fake ID like homebrew. The same goes for a frame dump's ID.

Combines #22419 and #22420 by Acts1631.

Co-Authored-By: Acts1631 <acts1631kjv@proton.me>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:39:06 -06:00
Henrik Rydgård f3d7d8bc0c Fix Zip Slip in zip extraction and add unit test
A crafted zip with a parent-directory ("..") entry name could escape the
destination directory during extraction, writing arbitrary files on the
host (e.g. into startup/autostart folders). ExtractZipContents built the
output path by concatenating the raw zip entry name onto the destination
with no traversal check.

Changes:
- Add HasParentDirComponent() utility in Core/Util/PathUtil and use it in
  GameManager::ExtractZipContents to reject entries with a ".." component.
  Guard both the directory-creation and file-writing passes.
- Expose ExtractZipContents as public for testing.
- Add unittest/TestZipSlip which crafts a zip with a "../evil.txt" entry
  and verifies it is not written outside the destination directory.
2026-07-31 20:35:12 +02:00