feat: enhance local-dev with PR review & MinIO S3#1975
feat: enhance local-dev with PR review & MinIO S3#1975Asi0Flammeus wants to merge 2 commits intodevfrom
Conversation
- Add --content-pr <number> to switch content to any PR branch (handles forks) - Add --content-reset to restore upstream repo + main branch - Restart dev servers on content env change (API reads config at boot) - Surface sync errors (e.g. missing fields) after sync completes - Reset all branches to dev on `down` for clean slate - Show fork indicator in status and branch menu Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Local sync was producing 54 ECONNREFUSED errors because no S3 service was available. This adds MinIO as a local S3-compatible store, eliminating all S3 errors and enabling assignment PDF upload/download in local dev. - local-dev.sh: manage MinIO container lifecycle (up/down/nuke/status), auto-create bucket, patch S3 vars on first run - S3 client: auto-detect non-AWS endpoints and use path-style addressing (required by MinIO and all S3-compatible stores) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| secretAccessKey: config.secretKey, | ||
| }, | ||
| endpoint: config.endpoint, | ||
| forcePathStyle: !config.endpoint.includes('amazonaws.com'), |
Check failure
Code scanning / CodeQL
Incomplete URL substring sanitization High
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 6 days ago
In general, the problem is that the code checks whether the string "amazonaws.com" appears anywhere in config.endpoint, instead of reliably determining whether the endpoint really belongs to AWS. To fix this, we should parse the endpoint as a URL, extract its hostname, normalize it, and then compare it against a whitelist pattern, such as “is the hostname exactly amazonaws.com or ends with .amazonaws.com”. For plain hostnames without scheme, we can normalize by prepending https:// before parsing.
The best minimal fix here is to replace the .includes('amazonaws.com') call with a small helper that safely determines whether an endpoint is an AWS S3 endpoint. That helper can (1) accept the endpoint string, (2) ensure it has a scheme (prepend https:// if missing), (3) use the standard URL class to parse it, (4) extract hostname, and (5) return true only if the hostname is amazonaws.com or ends with .amazonaws.com. We then compute a boolean (e.g. isAwsEndpoint) and set forcePathStyle based on its negation as before, preserving behavior for genuine AWS endpoints but avoiding substring‑based misclassification for arbitrary URLs. This change can be implemented entirely inside packages/s3/src/index.ts without new external dependencies, using the built‑in URL global.
Concretely:
- Add a small helper function (e.g.
isAwsEndpoint) abovecreateS3Service, within the same file. - In that helper, normalize and parse the endpoint string and perform host comparison as described.
- In
createS3Service, replaceforcePathStyle: !config.endpoint.includes('amazonaws.com')withforcePathStyle: !isAwsEndpoint(config.endpoint).
| @@ -58,6 +58,28 @@ | ||
| return path.replaceAll(/\/+/g, '/'); | ||
| }; | ||
|
|
||
| const isAwsEndpoint = (endpoint: string): boolean => { | ||
| if (!endpoint) { | ||
| return false; | ||
| } | ||
|
|
||
| let urlString = endpoint; | ||
| if (!/^[a-zA-Z][a-zA-Z0-9+\-.]*:/.test(urlString)) { | ||
| urlString = `https://${urlString}`; | ||
| } | ||
|
|
||
| try { | ||
| const url = new URL(urlString); | ||
| const hostname = url.hostname.toLowerCase(); | ||
| return ( | ||
| hostname === 'amazonaws.com' || | ||
| hostname.endsWith('.amazonaws.com') | ||
| ); | ||
| } catch { | ||
| return false; | ||
| } | ||
| }; | ||
|
|
||
| export const createS3Service = (config: S3Config): S3Service => { | ||
| const s3 = new S3Client({ | ||
| credentials: { | ||
| @@ -65,7 +87,7 @@ | ||
| secretAccessKey: config.secretKey, | ||
| }, | ||
| endpoint: config.endpoint, | ||
| forcePathStyle: !config.endpoint.includes('amazonaws.com'), | ||
| forcePathStyle: !isAwsEndpoint(config.endpoint), | ||
| region: config.region, | ||
| }); | ||
|
|
Summary
Extends local-dev.sh with two capabilities that make local development self-sufficient:
PR review support (57ea0e6)
local-dev.sh branch --content <branch>switches the content branch and re-syncslocal-dev.sh syncnow reports error counts, enabling agent-driven review workflowsMinIO S3 storage (81b228a)
Result
Test plan