Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/runtime/providers/ipxStatic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ export default defineProvider<Partial<IPXOptions>>({
}

const params = operationsGenerator(modifiers) || '_'
const encodedSrc = src.replace(/\?/g, '%3F').replace(/&/g, '%26').replace(/=/g, '%3D')

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# Read the relevant file to see lines 17 and 24 in context
sed -n '1,35p' src/runtime/providers/ipxStatic.ts

Repository: nuxt/image

Length of output: 906


🏁 Script executed:

# Find where encodePath is imported from
rg "encodePath" src/runtime/providers/ipxStatic.ts -B 5 -A 2

Repository: nuxt/image

Length of output: 378


🏁 Script executed:

# Check the ufo library documentation or source to understand encodePath behavior
rg "export.*encodePath|function encodePath" --type ts --type js

Repository: nuxt/image

Length of output: 36


🏁 Script executed:

# Check all usages of encodePath in the codebase
rg "encodePath\(" --type ts --type js -B 2 -A 1

Repository: nuxt/image

Length of output: 2574


🏁 Script executed:

# Check if there's a package.json to see ufo version and potentially documentation
cat package.json | grep -A 2 '"ufo"'

Repository: nuxt/image

Length of output: 102


🏁 Script executed:

# Look for any existing patterns of encoding/pre-encoding in other providers
rg "replace.*%[0-9A-F]" src/runtime/providers/ --type ts

Repository: nuxt/image

Length of output: 561


🏁 Script executed:

# Check ufo library - look at its exported functions or any type definitions
npm list ufo 2>/dev/null || echo "npm not available, checking if there's documentation"

# Try to find any documentation or examples of encodePath behavior
rg "encodePath" . --type md --type txt 2>/dev/null || true

Repository: nuxt/image

Length of output: 17342


🏁 Script executed:

# Let's look at how glide.ts uses encodePath - it's a simpler case
cat -n src/runtime/providers/glide.ts | grep -A 5 -B 5 "encodePath"

Repository: nuxt/image

Length of output: 731


🏁 Script executed:

# Compare ipxStatic with ipx to understand the difference
echo "=== IPX.TS (lines 75-85) ===" && sed -n '75,85p' src/runtime/providers/ipx.ts
echo ""
echo "=== IPXSTATIC.TS (lines 12-28) ===" && sed -n '12,28p' src/runtime/providers/ipxStatic.ts

Repository: nuxt/image

Length of output: 699


🏁 Script executed:

# Let's look at the ufo library's encodePath implementation directly
find node_modules -name "*.d.ts" -o -name "*.ts" | xargs grep -l "encodePath" | grep ufo | head -1 | xargs cat | head -100

Repository: nuxt/image

Length of output: 2758


🏁 Script executed:

# Search for ufo source code or type definitions
cat node_modules/.pnpm/ufo@1.6.3/node_modules/ufo/dist/index.d.ts 2>/dev/null | grep -A 5 "encodePath"

Repository: nuxt/image

Length of output: 1387


🏁 Script executed:

# Let's test the actual behavior by creating a simple test
python3 << 'EOF'
# Standard URL encoding behavior
from urllib.parse import quote

src = "https://example.com/image?param=value&other=test"

# Pre-encoding approach (like ipxStatic does)
pre_encoded = src.replace("?", "%3F").replace("&", "%26").replace("=", "%3D")
print(f"Pre-encoded: {pre_encoded}")

# Then encoding again (what encodePath would do)
double_encoded = quote(pre_encoded, safe='/:')
print(f"After second encoding: {double_encoded}")

# Direct encoding approach (like ipx does)
direct = quote(src, safe='/:')
print(f"Direct encoding: {direct}")

print(f"\nDo they match? {double_encoded == direct}")
EOF

Repository: nuxt/image

Length of output: 308


🏁 Script executed:

# Get the actual implementation of encodePath in ufo
cat node_modules/.pnpm/ufo@1.6.3/node_modules/ufo/dist/index.js | grep -A 10 "function encodePath"

Repository: nuxt/image

Length of output: 146


🏁 Script executed:

# Let's also check the actual source code or README to understand the intent
cat node_modules/.pnpm/ufo@1.6.3/node_modules/ufo/README.md 2>/dev/null | grep -A 20 "encodePath" || echo "No README with encodePath info"

Repository: nuxt/image

Length of output: 1433


Remove manual pre-encoding before encodePath to prevent double-encoding.

Line 17 pre-encodes ?, &, = into %3F, %26, %3D. When line 24 then calls encodePath(encodedSrc), the % character gets encoded again to %25, producing %253F, %2526, %253D. This breaks URL reconstruction.

The correct pattern is used in ipx.ts (line 80): call encodePath(src) directly without pre-encoding. If = must be encoded separately, do so only after the single encoding pass.

Proposed fix
-    const encodedSrc = src.replace(/\?/g, '%3F').replace(/&/g, '%26').replace(/=/g, '%3D')
+    const encodedSrc = encodePath(src).replace(/=/g, '%3D')

     if (!baseURL) {
       baseURL = joinURL(ctx.options.nuxt.baseURL, '/_ipx')
     }

     return {
-      url: joinURL(baseURL, params, encodePath(encodedSrc).replace(/\/{2,}/g, '/')),
+      url: joinURL(baseURL, params, encodedSrc.replace(/\/{2,}/g, '/')),
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/runtime/providers/ipxStatic.ts` at line 17, Remove the manual
pre-encoding step that replaces ?, &, = before calling encodePath; specifically,
stop constructing encodedSrc with src.replace(...) and instead pass the original
src into encodePath (i.e., call encodePath(src)). If you still need to specially
encode '=' for this provider, perform that replacement only after encodePath has
been applied. Update references in this file to use the unmodified src with
encodePath and remove the pre-encoding logic around encodedSrc.


if (!baseURL) {
baseURL = joinURL(ctx.options.nuxt.baseURL, '/_ipx')
}

return {
url: joinURL(baseURL, params, encodePath(src).replace(/\/{2,}/g, '/')),
url: joinURL(baseURL, params, encodePath(encodedSrc).replace(/\/{2,}/g, '/')),
}
},
})
Loading