Skip to content

chore: media plugin improvements#111

Merged
olliethedev merged 3 commits intomainfrom
fix/media-plugin-local-adapter-export
Apr 9, 2026
Merged

chore: media plugin improvements#111
olliethedev merged 3 commits intomainfrom
fix/media-plugin-local-adapter-export

Conversation

@olliethedev
Copy link
Copy Markdown
Collaborator

@olliethedev olliethedev commented Apr 9, 2026

Summary

  • fix: local adapter as a standalone export

Type of change

  • Bug fix
  • New plugin
  • Feature / enhancement to an existing plugin
  • Documentation
  • Chore / refactor / tooling

Checklist

  • pnpm build passes
  • pnpm typecheck passes
  • pnpm lint passes
  • Tests added or updated (unit and/or E2E)
  • Docs updated (docs/content/docs/) if consumer-facing types or behavior changed
  • All three codegen-projects create successfully and pass E2E tests
  • New plugin: submission checklist in CONTRIBUTING.md completed

Screenshots


Note

Medium Risk
Medium risk because it changes the public package surface (new export path for localAdapter and removal from plugins/media/api) and adds stricter file-path validation in the local adapter delete flow, which could affect existing consumers relying on previous imports or permissive deletion behavior.

Overview
Makes localAdapter a standalone export by adding ./plugins/media/api/adapters/local to build entries and package.json exports/typesVersions, and updating generated code templates to import it from the new path instead of plugins/media/api.

Hardens local file deletion in localAdapter.delete() by resolving uploadDir and rejecting decoded filenames that would escape the upload directory (path traversal), with a new unit test covering the malicious URL case.

Expands the media plugin programmatic API surface by exposing createAsset, updateAsset, and createFolder on mediaBackendPlugin().api (in addition to existing getters).

Reviewed by Cursor Bugbot for commit 719dbc0. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
better-stack-docs Ready Ready Preview, Comment Apr 9, 2026 1:55am
better-stack-playground Ready Ready Preview, Comment Apr 9, 2026 1:55am

Request Review

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Security Review — PR #111

Result: No high-confidence vulnerabilities introduced by this PR.

This PR is a pure package-export refactoring: localAdapter moves from the @btst/stack/plugins/media/api barrel to its own entry point @btst/stack/plugins/media/api/adapters/local. The implementation in local.ts is byte-for-byte identical between the base and head commits. No authentication, authorization, serialization, secret-handling, or dependency changes are present in the diff.


Pre-existing observation (not introduced by this PR)

While reviewing local.ts in context, one pre-existing weakness is worth noting now that this adapter is a more visible standalone export.

Path traversal in delete() lacks an upload-directory boundary check.

The delete method reconstructs a file path by splitting the stored URL on /, decoding the last segment, and joining it with uploadDir:

const encodedFilename = url.split("/").pop();
const filename = decodeURIComponent(encodedFilename);
const filePath = path.join(uploadDir, filename);   // no boundary check
await fs.unlink(filePath);

If a URL such as /uploads/..%2F..%2Fetc%2Fpasswd were ever stored in the database, decoding it yields ../../etc/passwd and path.join resolves outside uploadDir. In practice the upload path generates filenames via path.basename() + random bytes so normal writes are safe — but there is no guard in delete() itself to assert filePath.startsWith(path.resolve(uploadDir)).

Exploitability: Low under normal operation (URLs are written by the trusted upload path). Becomes a real risk if records can be seeded through DB migrations, admin tooling, or a future code path that writes arbitrary URLs.

Suggested remediation (for a follow-up):

const resolvedUploadDir = path.resolve(uploadDir);
const filePath = path.join(resolvedUploadDir, filename);
if (!filePath.startsWith(resolvedUploadDir + path.sep)) {
  throw new Error("Refusing to delete file outside upload directory");
}
await fs.unlink(filePath);

This PR can be merged safely as written. The observation above is pre-existing and should be tracked as a separate hardening task.

Open in Web View Automation 

Sent by Cursor Automation: Find vulnerabilities

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 9, 2026

Shadcn registry validated — no registry changes detected.

@olliethedev olliethedev changed the title fix:mark the local adapter in media plugin as a standalone export chore: media plugin improvements Apr 9, 2026
@olliethedev olliethedev merged commit 335b880 into main Apr 9, 2026
9 checks passed
@olliethedev olliethedev deleted the fix/media-plugin-local-adapter-export branch April 9, 2026 13:56
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