fix: Tighten share-URL validation against malicious color strings#60
Merged
fix: Tighten share-URL validation against malicious color strings#60
Conversation
`validateShareData` previously only checked `version === 1` and the
truthiness of `grid`/`colors`, so a crafted share URL could set
`patternColors` to arbitrary strings. Those strings flow into
`exportSvg` as `fill="${color}"` via string concatenation, so a payload
like `"/><script>...</script>` in a downloaded SVG would execute when
the victim opens the file directly in a browser.
Strict-validate share data at the boundary: hex-regex each color in
`colors.pattern`, `colors.background`, and `palette.custom`; require
plain objects and finite numeric dimensions; require array-of-arrays
shape for cells. Also run `sanitizeGrid` on share-URL cells to match
the file-import path (export.js:817). Promoted `sanitizeGrid` to an
exported helper.
3 tasks
Itroun
added a commit
that referenced
this pull request
Apr 23, 2026
* deps(deps-dev): Bump the patch-updates group with 2 updates (#52) Bumps the patch-updates group with 2 updates: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vite` from 8.0.4 to 8.0.8 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite) Updates `vitest` from 4.1.2 to 4.1.4 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: vitest dependency-version: 4.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Deploy directly in dependabot workflow to bypass GITHUB_TOKEN limitation GITHUB_TOKEN merges don't trigger other workflows, so deploy-production.yml never fires after dependabot PRs are merged to main. The dependabot deploy workflow now builds and deploys to Cloudflare Pages directly after merging, and syncs main back into staging. Also adds workflow_dispatch to deploy-production.yml for manual triggers. * deps(deps-dev): Bump vite from 8.0.8 to 8.0.9 in the patch-updates group (#55) Bumps the patch-updates group with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 8.0.8 to 8.0.9 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.9/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * deps(deps-dev): Bump happy-dom from 20.8.9 to 20.9.0 (#56) Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 20.8.9 to 20.9.0. - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](capricorn86/happy-dom@v20.8.9...v20.9.0) --- updated-dependencies: - dependency-name: happy-dom dependency-version: 20.9.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Harden dependabot auto-deploy against back-to-back PRs (#59) Back-to-back dependabot PRs landing on staging could race the dependabot-deploy workflow: the second staging→main PR would enable `gh pr merge --auto`, which silently no-ops when no required check is pending, leaving the PR stuck and the workflow timing out. Three defenses: - Group minor updates alongside patches so a typical week produces one combined PR instead of several. - Try an immediate squash merge first and only fall back to --auto when the PR isn't yet mergeable, so --auto's no-op behavior can't strand a ready PR. - Add a concurrency group so overlapping deploy runs queue instead of racing each other. * fix: Tighten share-URL validation against malicious color strings (#60) `validateShareData` previously only checked `version === 1` and the truthiness of `grid`/`colors`, so a crafted share URL could set `patternColors` to arbitrary strings. Those strings flow into `exportSvg` as `fill="${color}"` via string concatenation, so a payload like `"/><script>...</script>` in a downloaded SVG would execute when the victim opens the file directly in a browser. Strict-validate share data at the boundary: hex-regex each color in `colors.pattern`, `colors.background`, and `palette.custom`; require plain objects and finite numeric dimensions; require array-of-arrays shape for cells. Also run `sanitizeGrid` on share-URL cells to match the file-import path (export.js:817). Promoted `sanitizeGrid` to an exported helper. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
github-actions Bot
added a commit
that referenced
this pull request
Apr 27, 2026
* deps(deps-dev): Bump the patch-updates group with 2 updates (#52) Bumps the patch-updates group with 2 updates: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vite` from 8.0.4 to 8.0.8 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite) Updates `vitest` from 4.1.2 to 4.1.4 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: vitest dependency-version: 4.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Deploy directly in dependabot workflow to bypass GITHUB_TOKEN limitation GITHUB_TOKEN merges don't trigger other workflows, so deploy-production.yml never fires after dependabot PRs are merged to main. The dependabot deploy workflow now builds and deploys to Cloudflare Pages directly after merging, and syncs main back into staging. Also adds workflow_dispatch to deploy-production.yml for manual triggers. * deps(deps-dev): Bump vite from 8.0.8 to 8.0.9 in the patch-updates group (#55) Bumps the patch-updates group with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 8.0.8 to 8.0.9 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.9/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * deps(deps-dev): Bump happy-dom from 20.8.9 to 20.9.0 (#56) Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 20.8.9 to 20.9.0. - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](capricorn86/happy-dom@v20.8.9...v20.9.0) --- updated-dependencies: - dependency-name: happy-dom dependency-version: 20.9.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Harden dependabot auto-deploy against back-to-back PRs (#59) Back-to-back dependabot PRs landing on staging could race the dependabot-deploy workflow: the second staging→main PR would enable `gh pr merge --auto`, which silently no-ops when no required check is pending, leaving the PR stuck and the workflow timing out. Three defenses: - Group minor updates alongside patches so a typical week produces one combined PR instead of several. - Try an immediate squash merge first and only fall back to --auto when the PR isn't yet mergeable, so --auto's no-op behavior can't strand a ready PR. - Add a concurrency group so overlapping deploy runs queue instead of racing each other. * fix: Tighten share-URL validation against malicious color strings (#60) `validateShareData` previously only checked `version === 1` and the truthiness of `grid`/`colors`, so a crafted share URL could set `patternColors` to arbitrary strings. Those strings flow into `exportSvg` as `fill="${color}"` via string concatenation, so a payload like `"/><script>...</script>` in a downloaded SVG would execute when the victim opens the file directly in a browser. Strict-validate share data at the boundary: hex-regex each color in `colors.pattern`, `colors.background`, and `palette.custom`; require plain objects and finite numeric dimensions; require array-of-arrays shape for cells. Also run `sanitizeGrid` on share-URL cells to match the file-import path (export.js:817). Promoted `sanitizeGrid` to an exported helper. * deps(deps-dev): Bump the patch-updates group with 2 updates (#62) Bumps the patch-updates group with 2 updates: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vite` from 8.0.9 to 8.0.10 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.10/packages/vite) Updates `vitest` from 4.1.4 to 4.1.5 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: vitest dependency-version: 4.1.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Itroun <78351901+Itroun@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Itroun
added a commit
that referenced
this pull request
Apr 27, 2026
* deps(deps-dev): Bump the patch-updates group with 2 updates (#52) Bumps the patch-updates group with 2 updates: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vite` from 8.0.4 to 8.0.8 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.8/packages/vite) Updates `vitest` from 4.1.2 to 4.1.4 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.4/packages/vitest) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.8 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: vitest dependency-version: 4.1.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Deploy directly in dependabot workflow to bypass GITHUB_TOKEN limitation GITHUB_TOKEN merges don't trigger other workflows, so deploy-production.yml never fires after dependabot PRs are merged to main. The dependabot deploy workflow now builds and deploys to Cloudflare Pages directly after merging, and syncs main back into staging. Also adds workflow_dispatch to deploy-production.yml for manual triggers. * deps(deps-dev): Bump vite from 8.0.8 to 8.0.9 in the patch-updates group (#55) Bumps the patch-updates group with 1 update: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite). Updates `vite` from 8.0.8 to 8.0.9 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.9/packages/vite) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.9 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * deps(deps-dev): Bump happy-dom from 20.8.9 to 20.9.0 (#56) Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 20.8.9 to 20.9.0. - [Release notes](https://github.com/capricorn86/happy-dom/releases) - [Commits](capricorn86/happy-dom@v20.8.9...v20.9.0) --- updated-dependencies: - dependency-name: happy-dom dependency-version: 20.9.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Harden dependabot auto-deploy against back-to-back PRs (#59) Back-to-back dependabot PRs landing on staging could race the dependabot-deploy workflow: the second staging→main PR would enable `gh pr merge --auto`, which silently no-ops when no required check is pending, leaving the PR stuck and the workflow timing out. Three defenses: - Group minor updates alongside patches so a typical week produces one combined PR instead of several. - Try an immediate squash merge first and only fall back to --auto when the PR isn't yet mergeable, so --auto's no-op behavior can't strand a ready PR. - Add a concurrency group so overlapping deploy runs queue instead of racing each other. * fix: Tighten share-URL validation against malicious color strings (#60) `validateShareData` previously only checked `version === 1` and the truthiness of `grid`/`colors`, so a crafted share URL could set `patternColors` to arbitrary strings. Those strings flow into `exportSvg` as `fill="${color}"` via string concatenation, so a payload like `"/><script>...</script>` in a downloaded SVG would execute when the victim opens the file directly in a browser. Strict-validate share data at the boundary: hex-regex each color in `colors.pattern`, `colors.background`, and `palette.custom`; require plain objects and finite numeric dimensions; require array-of-arrays shape for cells. Also run `sanitizeGrid` on share-URL cells to match the file-import path (export.js:817). Promoted `sanitizeGrid` to an exported helper. * deps(deps-dev): Bump the patch-updates group with 2 updates (#62) Bumps the patch-updates group with 2 updates: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) and [vitest](https://github.com/vitest-dev/vitest/tree/HEAD/packages/vitest). Updates `vite` from 8.0.9 to 8.0.10 - [Release notes](https://github.com/vitejs/vite/releases) - [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md) - [Commits](https://github.com/vitejs/vite/commits/v8.0.10/packages/vite) Updates `vitest` from 4.1.4 to 4.1.5 - [Release notes](https://github.com/vitest-dev/vitest/releases) - [Commits](https://github.com/vitest-dev/vitest/commits/v4.1.5/packages/vitest) --- updated-dependencies: - dependency-name: vite dependency-version: 8.0.10 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates - dependency-name: vitest dependency-version: 4.1.5 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: patch-updates ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * fix: Fetch full history when checking out main for back-merge (#64) The dependabot-deploy workflow's post-deploy "Sync main into staging" step was failing with "refusing to merge unrelated histories" because the preceding `Checkout main (post-merge)` step used the default shallow clone (depth=1). With no shared history fetched, git cannot find a common ancestor between origin/main and origin/staging and refuses the merge. Set fetch-depth: 0 on that checkout so the back-merge succeeds. --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
validateShareDatapreviously only checkedversion === 1and truthiness ofgrid/colors. A crafted share URL could setpatternColorsto arbitrary attacker-controlled strings, which then flow intoexportSvgasfill=\"\${color}\"via string concatenation — a payload like\"/><script>...</script>in a downloaded SVG would execute when the victim opens the file directly in a browser.colors.pattern,colors.background, andpalette.custom; require plain objects and finite numeric dimensions; require 2D-array shape for cells.sanitizeGridon share-URL cells to match the existing file-import path; promotedsanitizeGridto an exported helper. ExtractedHEX_COLOR_PATTERNtovalidation.jsas the single source of truth.Test plan
npm test— 193/193 unit tests pass (4 new cases for non-hex colors, bad dims, malformed cells, bad palette)