Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions code/core/src/manager-api/modules/refs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,8 @@ export const getSourceType = (source: string, refId?: string) => {
const { origin: localOrigin, pathname: localPathname } = location;
const { origin: sourceOrigin, pathname: sourcePathname } = new URL(source);

const localFull = `${localOrigin + localPathname}`.replace('/iframe.html', '').replace(/\/$/, '');
const sourceFull = `${sourceOrigin + sourcePathname}`
.replace('/iframe.html', '')
.replace(/\/$/, '');
const localFull = `${localOrigin + localPathname}`.replace(/\/[^\/]*$/, '');
const sourceFull = `${sourceOrigin + sourcePathname}`.replace(/\/[^\/]*$/, '');
Comment on lines +84 to +85
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n code/core/src/manager-api/modules/refs.ts | head -100

Repository: storybookjs/storybook

Length of output: 3877


🏁 Script executed:

# Check where getSourceType is used
rg "getSourceType" code/core/src/manager-api/modules/refs.ts -B 2 -A 2

# Search for tests of this function
fd "refs" code/core --type f -name "*.test.ts" -o -name "*.spec.ts" | head -5

Repository: storybookjs/storybook

Length of output: 529


🏁 Script executed:

# Find test files for refs
fd "refs" code/core -name "*test*" -o -name "*spec*"

# Search for usage of getSourceType
rg "getSourceType" code/core --type ts --type tsx

Repository: storybookjs/storybook

Length of output: 323


🏁 Script executed:

# Find test files for refs module
find code/core -name "*refs*test*" -o -name "*refs*spec*"

# Search for getSourceType usage with correct rg syntax
rg "getSourceType" code/core

Repository: storybookjs/storybook

Length of output: 1135


🏁 Script executed:

cat code/core/src/manager-api/tests/refs.test.ts

Repository: storybookjs/storybook

Length of output: 34831


🏁 Script executed:

# Let me trace through the regex behavior with concrete examples
python3 << 'EOF'
import re

# Test the regex behavior
regex = r'/[^/]*$'

test_cases = [
    'https://storybook.js.org/storybook/iframe.html',
    'https://storybook.js.org/storybook/',
    'https://storybook.js.org/storybook',
    'https://external.com/storybook/',
    'https://external.com/storybook',
]

for url in test_cases:
    result = re.sub(regex, '', url)
    print(f"{url:50} → {result}")
EOF

Repository: storybookjs/storybook

Length of output: 151


🏁 Script executed:

# Let's manually verify the regex behavior with simple shell commands
echo "Testing regex replacement /\/[^\/]*$/:"
echo ""
echo "Input: https://storybook.js.org/storybook/iframe.html"
echo "https://storybook.js.org/storybook/iframe.html" | sed 's/\/[^\/]*$//'
echo ""
echo "Input: https://storybook.js.org/storybook/"
echo "https://storybook.js.org/storybook/" | sed 's/\/[^\/]*$//'
echo ""
echo "Input: https://storybook.js.org/storybook"
echo "https://storybook.js.org/storybook" | sed 's/\/[^\/]*$//'
echo ""
echo "Input: https://external.com/storybook/"
echo "https://external.com/storybook/" | sed 's/\/[^\/]*$//'

Repository: storybookjs/storybook

Length of output: 411


🏁 Script executed:

# Check if tests cover the scenario: location with trailing slash vs ref without trailing slash
grep -n "https://storybook.js.org/storybook'" code/core/src/manager-api/tests/refs.test.ts | head -20

Repository: storybookjs/storybook

Length of output: 166


🏁 Script executed:

# Check the context around those line numbers to understand what's being tested
sed -n '140,170p' code/core/src/manager-api/tests/refs.test.ts

Repository: storybookjs/storybook

Length of output: 1352


🏁 Script executed:

# Let me trace the exact scenario the review comment mentions
# Location: /storybook/ (trailing slash)
# Source ref: /storybook (no trailing slash, no filename)

echo "Scenario from review comment:"
echo "Manager location: https://storybook.js.org/storybook/ (with trailing slash)"
echo "Ref source: https://storybook.js.org/storybook (no trailing slash, no filename)"
echo ""
echo "After regex /\/[^\/]*$/:"
echo "localFull: https://storybook.js.org/storybook/ →" $(echo 'https://storybook.js.org/storybook/' | sed 's/\/[^\/]*$//')
echo "sourceFull: https://storybook.js.org/storybook →" $(echo 'https://storybook.js.org/storybook' | sed 's/\/[^\/]*$//')
echo ""
echo "Result: These DO NOT MATCH, so getSourceType() returns 'external' (INCORRECT)"
echo ""
echo "This edge case is NOT covered in the test suite."

Repository: storybookjs/storybook

Length of output: 563


The base-path normalization logic has a trailing-slash edge case that could cause refs to be misclassified.

The regex at lines 84–85 removes the last path segment including its slash. This means:

  • https://host/storybook/ normalizes to https://host/storybook
  • https://host/storybook normalizes to https://host

If a ref is configured without a trailing slash (e.g., https://host/storybook) but the manager runs at a path with a trailing slash (e.g., https://host/storybook/), they will normalize to different values and the ref will be incorrectly classified as external instead of local. This scenario is not covered by the current test suite.

🤖 Prompt for AI Agents
In `@code/core/src/manager-api/modules/refs.ts` around lines 84 - 85, The
normalization currently builds localFull/sourceFull from
localOrigin+localPathname and then strips the last path segment with
.replace(/\/[^\/]*$/,''), which collapses a trailing slash form
(https://host/storybook/) to https://host/storybook but turns
https://host/storybook into https://host; change the logic in the localFull and
sourceFull construction to normalize by removing only a trailing slash (e.g.,
replace(/\/$/,'')) or, better, use the URL API to join origin+pathname and then
trim a single trailing slash so both https://host/storybook and
https://host/storybook/ normalize to the same value; update the expressions that
compute localFull and sourceFull (and any callers that rely on them)
accordingly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I tested with refs with both trailing and non-trailing and both worked, the ref adds /index.html to the link to external storybook properly in both cases.


if (localFull === sourceFull) {
return ['local', sourceFull];
Expand Down