π§ update: update dependencies and add Pexels background image support - #36
π§ update: update dependencies and add Pexels background image support#36warengonzaga wants to merge 7 commits into
Conversation
* β chore: Bump @types/node from 22.19.19 to 25.9.1 Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.19 to 25.9.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node) --- updated-dependencies: - dependency-name: "@types/node" dependency-version: 25.9.1 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * β chore: update bun lockfile --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Waren Gonzaga <opensource@warengonzaga.com>
* β chore: Bump @hono/node-server from 1.19.14 to 2.0.4 Bumps [@hono/node-server](https://github.com/honojs/node-server) from 1.19.14 to 2.0.4. - [Release notes](https://github.com/honojs/node-server/releases) - [Commits](honojs/node-server@v1.19.14...v2.0.4) --- updated-dependencies: - dependency-name: "@hono/node-server" dependency-version: 2.0.4 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * β chore: update bun lockfile --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Waren Gonzaga <opensource@warengonzaga.com>
* β chore: Bump typescript from 5.9.3 to 6.0.3 Bumps [typescript](https://github.com/microsoft/TypeScript) from 5.9.3 to 6.0.3. - [Release notes](https://github.com/microsoft/TypeScript/releases) - [Commits](microsoft/TypeScript@v5.9.3...v6.0.3) --- updated-dependencies: - dependency-name: typescript dependency-version: 6.0.3 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * β chore: update bun lockfile * βοΈ setup: add ignoreDeprecations for typescript 6 compatibility --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Waren Gonzaga <opensource@warengonzaga.com>
* π¦ new: add custom background image support with Pexels integration * π§ update: fix biome formatting for contentType assignment * π security: harden image fetch and fix review findings * π security: block redirects, reject non-image responses, fix fallback * π docs: document PEXELS_API_KEY in .env.example * π security: block localhost hostnames and fix IPv6 prefix false positives --------- Co-authored-by: Waren Gonzaga <opensource@warengonzaga.com> Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds support for using remote background images in generated banners (via a new bgimg query param), plus an in-app Pexels search UI backed by a new proxy API route, alongside a set of dependency upgrades.
Changes:
- Add
bgimgbackground-image support (server-side fetch + base64 embed in SVG) with URL validation/SSRF mitigations. - Add
/api/pexels/searchproxy endpoint and UI widget to search/select Pexels images. - Upgrade core build/runtime dependencies (TypeScript, Node types,
@hono/node-server, etc.) and update docs/config.
Reviewed changes
Copilot reviewed 10 out of 12 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Adds TS ignoreDeprecations setting for TS 6.x. |
| src/utils/sanitize.ts | Adds isValidImageUrl and private-host checks for bgimg. |
| src/ui/index.html | Adds background image input and Pexels search widget + query param wiring. |
| src/routes/pexels.ts | New Pexels proxy search API route. |
| src/routes/banner.ts | Adds bgimg query param parsing and background selection logic. |
| src/index.ts | Registers the new Pexels route. |
| src/banner/types.ts | Extends BackgroundPreset to support image backgrounds. |
| src/banner/svg-template.ts | Implements server-side image fetch + base64 embedding for SVG backgrounds. |
| README.md | Documents bgimg and Pexels integration. |
| package.json | Upgrades dependencies/devDependencies. |
| bun.lock | Lockfile updates for dependency bumps. |
| .env.example | Adds PEXELS_API_KEY configuration. |
| "devDependencies": { | ||
| "@biomejs/biome": "^2.4.7", | ||
| "@types/node": "^22.10.0", | ||
| "@types/node": "^25.9.1", | ||
| "tsup": "^8.3.0", | ||
| "tsx": "^4.19.0", | ||
| "typescript": "^5.7.0" | ||
| "typescript": "^6.0.3" | ||
| }, |
warengonzaga
left a comment
There was a problem hiding this comment.
All reported implementation concerns have been acknowledged and remain open for code changes before resolution.
| const contentType = ALLOWED_IMAGE_TYPES.find((t) => | ||
| rawContentType.startsWith(t), | ||
| ); | ||
| if (!contentType) return null; |
There was a problem hiding this comment.
Acknowledged. Image responses should require an explicit allowlisted content type before embedding. Kept open pending the validation change.
| bgRect = `<image href="${dataUri}" x="0" y="0" width="${WIDTH}" height="${HEIGHT}" preserveAspectRatio="xMidYMid slice" />`; | ||
| } else { | ||
| bgRect = `<rect width="${WIDTH}" height="${HEIGHT}" fill="#1a1a1a" />`; | ||
| } |
There was a problem hiding this comment.
Acknowledged. The failure path should restore the documented default gradient. Kept open pending the fallback fix.
| } catch { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
Acknowledged. URLs with embedded credentials should be rejected before server-side fetches. Kept open pending the validation change.
| const url = `${PEXELS_API_URL}/search?query=${encodeURIComponent(query)}&page=${page}&per_page=${perPage}&orientation=${orientation}`; | ||
| const response = await fetch(url, { | ||
| headers: { Authorization: apiKey }, | ||
| }); |
There was a problem hiding this comment.
Acknowledged. The Pexels request needs a timeout to prevent stalled upstream calls. Kept open pending the fix.
|
|
||
| # Pexels Integration (optional - enables the in-UI image search widget) | ||
| # Get a free API key at https://www.pexels.com/api/ | ||
| # When unset, the /api/pexels/search endpoint returns 503 and the search is hidden |
There was a problem hiding this comment.
Acknowledged. The environment documentation should match the actual interface behavior. Kept open pending the documentation or UI fix.
| "tsx": "^4.19.0", | ||
| "typescript": "^5.7.0" | ||
| "typescript": "^6.0.3" | ||
| }, |
There was a problem hiding this comment.
Acknowledged. Node type definitions should align with the supported runtime major. Kept open pending the dependency correction.
|
@warengonzaga Na-push na ang dalawang verified fixes: may timeout na ang Pexels request at naitama na ang Pexels configuration note. Naiwan munang open ang iba pang code review items habang inaayos pa ang mga ito. |
| subheaderFontInput.value = 'Kinewave'; | ||
| bgInput.value = '1A1A1A'; | ||
| bgInput2.value = '4A4A4A'; | ||
| bgImgInput.value = ''; |
There was a problem hiding this comment.
π‘ Resetting the form leaves the Pexels search results and controls on screen
Clicking Reset clears the background image field (bgImgInput.value = '' at src/ui/index.html:2425) but does not clear the Pexels search box, thumbnail results, attribution line, or the Clear button, so the widget still shows stale search results that no longer match the reset banner.
Impact: After a reset, users see leftover Pexels image results and controls that look active but no longer correspond to the banner, which is confusing.
Reset handler updates only bgImgInput, not the Pexels widget state
The Pexels clear handler at src/ui/index.html:2388-2396 shows the full set of elements that need resetting (pexelsResults.innerHTML, pexelsResults.style.display, pexelsAttribution.style.display, pexelsClearBtn.style.display, pexelsQuery.value). The reset button handler at src/ui/index.html:2417-2458 only sets bgImgInput.value = '' and never touches these Pexels elements, so any previously rendered results grid, attribution text, Clear button, and search query remain visible after a reset.
Prompt for agents
In the reset button click handler in src/ui/index.html (around line 2417-2458), the code sets bgImgInput.value = '' but does not reset the Pexels search widget. To make the reset consistent with the dedicated Pexels clear handler (around lines 2388-2396), also clear pexelsQuery.value, set pexelsResults.innerHTML = '', set pexelsResults.style.display = 'none', set pexelsAttribution.style.display = 'none', and set pexelsClearBtn.style.display = 'none'. Note these Pexels element references are declared as consts inside the same script scope, so they are accessible from the reset handler.
Was this helpful? React with π or π to provide feedback.
|
@warengonzaga Na-push at local-test na ang tatlong natitirang background-image fixes: strict image content-type validation, pag-block ng URLs na may embedded credentials, at default gradient fallback kapag hindi ma-fetch ang image. Successful ang production build at focused validation checks. |
| export function isValidImageUrl(value: string): boolean { | ||
| if (!value || value.length > MAX_IMAGE_URL_LENGTH) return false; | ||
| try { | ||
| const url = new URL(value); | ||
| if (url.protocol !== 'https:') return false; | ||
| if (url.username || url.password) return false; | ||
| if (isPrivateHost(url.hostname)) return false; | ||
| return true; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
π¨ Server-side image fetch is vulnerable to SSRF via DNS rebinding
The new bgimg feature fetches an arbitrary user-supplied HTTPS URL server-side (fetchImageAsBase64 in src/banner/svg-template.ts:40-92). The URL is validated by isValidImageUrl (src/utils/sanitize.ts:169-180), which blocks private/loopback/link-local/metadata IP literals via isPrivateHost and enforces redirect: 'error'. However, validation only inspects the hostname string at request time; a public hostname that resolves to an internal IP (169.254.169.254, 10.x, etc.) at fetch time bypasses these checks (DNS rebinding). An attacker could point bgimg at an attacker-controlled domain resolving to cloud metadata or internal services, and while raw bodies are only re-embedded when they match allowed image content-types, response availability/timing and content-length can still leak internal-network information.
Was this helpful? React with π or π to provide feedback.
This pull request introduces a major new feature: support for custom background images in banners, including a built-in Pexels image search integration in the UI. Users can now specify any HTTPS image URL as a banner background, and optionally search for images directly from Pexels within the app. The changes span backend, frontend, documentation, and configuration to enable this functionality.
New background image support:
Added support for a new
bgimgquery parameter to allow any HTTPS image URL as the banner background. The image is fetched server-side, embedded as a base64 data URI in the SVG, and scaled to cover the banner area. Fallbacks and validation are included, with a maximum image size of 10 MB. (src/banner/svg-template.ts,src/banner/types.ts,src/routes/banner.ts,src/ui/index.html) [1] [2] [3] [4] [5] [6] [7] [8] [9]Updated the UI to include a new input for background image URLs, with clear priority over color/gradient backgrounds. (
src/ui/index.html) [1] [2] [3] [4]Pexels integration:
Added a new
/api/pexels/searchAPI route that proxies search requests to the Pexels API, enabling in-app search for landscape images. The API is enabled via a newPEXELS_API_KEYenvironment variable. (src/routes/pexels.ts,src/index.ts,.env.example) [1] [2] [3] [4]Enhanced the UI to provide a Pexels image search widget, allowing users to search, preview, and select images with a single click. Includes attribution and clear/reset functionality. (
src/ui/index.html) [1] [2]Documentation updates:
README.mdto document the newbgimgparameter, explain how to use custom background images, and describe the Pexels integration and its configuration. (README.md) [1] [2] [3] [4]Dependency updates:
@hono/node-server,@types/node, andtypescript, to their latest major versions. (package.json)