Core: Sanitize inputs for save from controls#33868
Conversation
|
View your CI Pipeline Execution ↗ for commit 4d8237d
☁️ Nx Cloud last updated this comment at |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review infoConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (2)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes ✨ Finishing Touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
code/core/src/core-server/utils/safeString.test.ts (1)
4-23: Consider adding test coverage for carriage return and other matched characters.The
safeJsStringregex matches\r(carriage return) and\t(tab), but no tests verify their escaping behavior. Adding tests for these would improve coverage and help catch regressions if the function is modified.🧪 Suggested additional tests
it('should skip escaping if not needed', () => { expect(safeJsString('./button.tsx')).toMatchInlineSnapshot(`"./button.tsx"`); }); + + it('should escape carriage return characters', () => { + expect(safeJsString('line1\rline2')).toMatchInlineSnapshot(`"line1\\rline2"`); + }); + + it('should escape tab characters', () => { + expect(safeJsString('col1\tcol2')).toMatchInlineSnapshot(`"col1\\tcol2"`); + }); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/core/src/core-server/utils/safeString.test.ts` around lines 4 - 23, Add unit tests for carriage return and tab escaping to cover characters matched by safeJsString's regex: add cases asserting safeJsString('const s = "\\rfoo"') and safeJsString('const s = "\\tfoo"') (and optionally a mixed string containing \r and \t together) produce the correctly escaped output; place them alongside the existing tests in safeString.test.ts referencing the safeJsString function so changes to the regex will be caught by CI.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@code/core/src/core-server/utils/safeString.ts`:
- Around line 6-14: The regex in the replace call inside safeString.ts includes
\b, \f and \t but the codes mapping object (codes) lacks entries for backspace,
form-feed and tab so those characters are left unescaped; update the codes
object used by the str.replace callback (the codes constant in the replace
handler) to include mappings for "\b": "\\b", "\f": "\\f" and "\t": "\\t" (or
alternatively remove those sequences from the regex), ensuring the replace
callback returns the proper escaped sequences for all characters matched by the
regex.
---
Nitpick comments:
In `@code/core/src/core-server/utils/safeString.test.ts`:
- Around line 4-23: Add unit tests for carriage return and tab escaping to cover
characters matched by safeJsString's regex: add cases asserting
safeJsString('const s = "\\rfoo"') and safeJsString('const s = "\\tfoo"') (and
optionally a mixed string containing \r and \t together) produce the correctly
escaped output; place them alongside the existing tests in safeString.test.ts
referencing the safeJsString function so changes to the regex will be caught by
CI.
Package BenchmarksCommit: The following packages have significant changes to their size or dependencies:
|
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 2 | 2 | 0 |
| Self size | 402 KB | 391 KB | 🎉 -11 KB 🎉 |
| Dependency size | 338 KB | 338 KB | 🎉 -3 B 🎉 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 535 | 535 | 0 |
| Self size | 648 KB | 648 KB | 🎉 -5 B 🎉 |
| Dependency size | 59.87 MB | 59.91 MB | 🚨 +36 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/nextjs-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 92 | 92 | 0 |
| Self size | 1.12 MB | 1.12 MB | 🎉 -41 B 🎉 |
| Dependency size | 22.39 MB | 22.43 MB | 🚨 +36 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-native-web-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 124 | 124 | 0 |
| Self size | 30 KB | 30 KB | 🚨 +37 B 🚨 |
| Dependency size | 23.68 MB | 23.71 MB | 🚨 +36 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-vite
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 82 | 82 | 0 |
| Self size | 35 KB | 35 KB | 🎉 -4 B 🎉 |
| Dependency size | 20.17 MB | 20.21 MB | 🚨 +36 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react-webpack5
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 274 | 274 | 0 |
| Self size | 24 KB | 24 KB | 🎉 -5 B 🎉 |
| Dependency size | 44.54 MB | 44.57 MB | 🚨 +36 KB 🚨 |
| Bundle Size Analyzer | Link | Link |
@storybook/react
| Before | After | Difference | |
|---|---|---|---|
| Dependency count | 58 | 57 | 🎉 -1 🎉 |
| Self size | 1.19 MB | 1.23 MB | 🚨 +37 KB 🚨 |
| Dependency size | 13.20 MB | 12.94 MB | 🎉 -264 KB 🎉 |
| Bundle Size Analyzer | Link | Link |
There was a problem hiding this comment.
Pull request overview
This pull request attempts to add input sanitization for the "create new story" feature by escaping special characters in file paths and export names. However, the implementation has critical flaws that would break the functionality.
Changes:
- Added a
safeJsStringutility function to escape special characters in strings - Added unit tests for the sanitization function
- Applied sanitization to
componentFilePathandcomponentExportNamein the create-new-story-channel handler
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| code/core/src/core-server/utils/safeString.ts | New utility function to escape JavaScript string special characters |
| code/core/src/core-server/utils/safeString.test.ts | Unit tests for the safeJsString function |
| code/core/src/core-server/server-channel/create-new-story-channel.ts | Integration of sanitization before passing data to generateStoryFile |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
code/core/src/core-server/utils/get-new-story-file.ts (1)
43-51:⚠️ Potential issue | 🟡 MinorDon’t reuse escaped basename for filesystem paths.
escapeForTemplate()inserts backslashes, which then flow intoalternativeStoryFileNameWithExtension. That changes the actual filename (and can introduce path separators on Windows), leading to unexpected file paths or failures when component names include',$, or\. Use a raw basename for filenames and an escaped one only for template interpolation.Suggested fix (separate raw vs. escaped)
const base = basename(componentFilePath); const extension = extname(componentFilePath); - const basenameWithoutExtension = escapeForTemplate(base.replace(extension, '')); + const rawBasenameWithoutExtension = base.replace(extension, ''); + const basenameWithoutExtension = escapeForTemplate(rawBasenameWithoutExtension); const dir = dirname(componentFilePath); const { storyFileName, isTypescript, storyFileExtension } = getStoryMetadata(componentFilePath); const storyFileNameWithExtension = `${storyFileName}.${storyFileExtension}`; - const alternativeStoryFileNameWithExtension = `${basenameWithoutExtension}.${componentExportName}.stories.${storyFileExtension}`; + const alternativeStoryFileNameWithExtension = `${rawBasenameWithoutExtension}.${componentExportName}.stories.${storyFileExtension}`;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@code/core/src/core-server/utils/get-new-story-file.ts` around lines 43 - 51, The code currently reuses basenameWithoutExtension (which has been run through escapeForTemplate) when building alternativeStoryFileNameWithExtension, causing backslashes and escape characters to end up in actual filenames; change to keep two values: a raw basenameWithoutExtensionRaw = base.replace(extension, '') for filesystem names and an escaped basenameWithoutExtensionEscaped = escapeForTemplate(basenameWithoutExtensionRaw) for template interpolation, then use basenameWithoutExtensionRaw when constructing alternativeStoryFileNameWithExtension and keep the escaped version only where templates need it (references: escapeForTemplate, alternativeStoryFileNameWithExtension, basenameWithoutExtension, base, componentExportName, getStoryMetadata).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@code/core/src/core-server/utils/get-new-story-file.ts`:
- Around line 43-51: The code currently reuses basenameWithoutExtension (which
has been run through escapeForTemplate) when building
alternativeStoryFileNameWithExtension, causing backslashes and escape characters
to end up in actual filenames; change to keep two values: a raw
basenameWithoutExtensionRaw = base.replace(extension, '') for filesystem names
and an escaped basenameWithoutExtensionEscaped =
escapeForTemplate(basenameWithoutExtensionRaw) for template interpolation, then
use basenameWithoutExtensionRaw when constructing
alternativeStoryFileNameWithExtension and keep the escaped version only where
templates need it (references: escapeForTemplate,
alternativeStoryFileNameWithExtension, basenameWithoutExtension, base,
componentExportName, getStoryMetadata).
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
code/core/src/core-server/utils/get-new-story-file.test.tscode/core/src/core-server/utils/get-new-story-file.tscode/core/src/core-server/utils/safeString.test.tscode/core/src/core-server/utils/safeString.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- code/core/src/core-server/utils/safeString.test.ts
…m-control-inputs Core: Sanitize inputs for save from controls (cherry picked from commit 12db183)
Closes #
What I did
Checklist for Contributors
Testing
The changes in this PR are covered in the following automated tests:
Manual testing
Caution
This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!
Documentation
MIGRATION.MD
Checklist for Maintainers
When this PR is ready for testing, make sure to add
ci:normal,ci:mergedorci:dailyGH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found incode/lib/cli-storybook/src/sandbox-templates.tsMake sure this PR contains one of the labels below:
Available labels
bug: Internal changes that fixes incorrect behavior.maintenance: User-facing maintenance tasks.dependencies: Upgrading (sometimes downgrading) dependencies.build: Internal-facing build tooling & test updates. Will not show up in release changelog.cleanup: Minor cleanup style change. Will not show up in release changelog.documentation: Documentation only changes. Will not show up in release changelog.feature request: Introducing a new feature.BREAKING CHANGE: Changes that break compatibility in some way with current major version.other: Changes that don't fit in the above categories.🦋 Canary release
This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the
@storybookjs/coreteam here.core team members can create a canary release here or locally with
gh workflow run --repo storybookjs/storybook publish.yml --field pr=<PR_NUMBER>Summary by CodeRabbit
Bug Fixes
Tests