Skip to content

Download card: Add download=true param to differentiate cross site requests#1737

Closed
jloh wants to merge 1 commit intomainfrom
modify_download_card
Closed

Download card: Add download=true param to differentiate cross site requests#1737
jloh wants to merge 1 commit intomainfrom
modify_download_card

Conversation

@jloh
Copy link
Copy Markdown
Member

@jloh jloh commented Feb 19, 2026

ref ONC-1478

  • This helps us differentiate when a file is being linked via a download card vs just hot linking and wanting to open in-browser
  • I've tested this via plain Nginx + via Ghost and the param doesn't change linking the file in normal operations

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 19, 2026

Walkthrough

The file card component in packages/kg-default-cards/lib/cards/file.js has been modified to append a query parameter to the anchor link's href attribute. The change adds ?download=true to the payload.src value in the download link, altering the download behavior without modifying the component's structure or other attributes.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: adding a download=true parameter to the download card to differentiate cross-site requests, which directly matches the changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the purpose of the download=true parameter and noting testing across different environments.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch modify_download_card

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…requests

ref ONC-1478

- This helps us differentiate when a file is being linked via a download card vs just hot linking and wanting to open in-browser
- I've tested this via plain Nginx + via Ghost and the param doesn't change linking the file in normal operations
@jloh jloh force-pushed the modify_download_card branch from 7111b2b to 8ae9b16 Compare February 19, 2026 11:53
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/kg-default-cards/lib/cards/file.js`:
- Line 44: The anchor tag unconditionally appends "?download=true" to
payload.src which breaks URLs that already contain a query string or fragment;
fix by computing a fullDownloadUrl local variable from payload.src that: 1)
strips or preserves fragments properly, 2) appends "?download=true" if there is
no existing query string or "&download=true" if there is one (ensuring the
fragment remains after the query), and then pass the fully-assembled
fullDownloadUrl into escapeHtml and use that escaped value in the href attribute
of the kg-file-card-container anchor; update the code that builds the href (and
any title/download attributes) to use this computed variable instead of escaping
payload.src directly.

let html = `
<div class="kg-card kg-file-card ${classNames}">
<a class="kg-file-card-container" href="${escapeHtml(payload.src)}" title="Download" download>
<a class="kg-file-card-container" href="${escapeHtml(payload.src)}?download=true" title="Download" download>
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, examine the actual file to understand the context
cat -n packages/kg-default-cards/lib/cards/file.js | head -80

Repository: TryGhost/Koenig

Length of output: 4619


🏁 Script executed:

# Search for tests related to file card or download=true
fd -e test.js -o -e spec.js -o -e tests.js | xargs rg -l "file.*card\|download" 2>/dev/null || echo "No test files found"

Repository: TryGhost/Koenig

Length of output: 212


🏁 Script executed:

# Look for test files in common locations
find . -type f \( -name "*.test.js" -o -name "*.spec.js" -o -name "*test*.js" \) -path "*/test/*" 2>/dev/null | head -20

Repository: TryGhost/Koenig

Length of output: 1143


🏁 Script executed:

# Search for any download=true references in the codebase
rg -n "download.*true\|download=true" --type=js -B2 -A2

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Search for URL handling utilities or helpers
rg -n "escapeHtml|formatUrl|appendQuery|setQuery" --type=js -A3 | head -50

Repository: TryGhost/Koenig

Length of output: 4160


🏁 Script executed:

# Check the file card test to see if there are tests for download=true or URLs with query params
cat packages/kg-default-cards/test/cards/file.test.js

Repository: TryGhost/Koenig

Length of output: 140


🏁 Script executed:

# Search for any URL with query params in test files
rg -n "\\?" packages/kg-default-cards/test/cards/file.test.js -C2

Repository: TryGhost/Koenig

Length of output: 148


🏁 Script executed:

# Check if there are any CDN or S3 URL examples in the codebase
rg -n "s3\|cdn\|cloudinary\|X-Amz" --type=js | head -20

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check how payload.src is typically set/used in examples
rg -n "payload\.src" packages/kg-default-cards --type=js -B1 -A1

Repository: TryGhost/Koenig

Length of output: 8980


🏁 Script executed:

# Look for any test files that might test the file card
find . -name "*.test.js" -o -name "*.spec.js" | xargs grep -l "file.*card\|kg-file" 2>/dev/null | head -10

Repository: TryGhost/Koenig

Length of output: 416


🏁 Script executed:

# Check the list of test files in kg-default-cards/test/cards to see what's available
ls -la packages/kg-default-cards/test/cards/

Repository: TryGhost/Koenig

Length of output: 1341


🏁 Script executed:

# Look at how the image card handles URLs with query params for comparison
cat packages/kg-default-cards/lib/cards/image.js | sed -n '110,115p'

Repository: TryGhost/Koenig

Length of output: 370


🏁 Script executed:

# Check if URL API is used elsewhere in the codebase for query param handling
rg -n "new URL\|searchParams\|append\|URLSearchParams" packages/kg-default-cards/lib/cards --type=js -B1 -A1

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check the file card test at kg-default-nodes
cat packages/kg-default-nodes/test/nodes/file.test.js | head -100

Repository: TryGhost/Koenig

Length of output: 4623


🏁 Script executed:

# Check the e2e test for file card
cat packages/koenig-lexical/test/e2e/cards/file-card.test.js | head -150

Repository: TryGhost/Koenig

Length of output: 5957


🏁 Script executed:

# Search specifically in these test files for download or href testing
rg -n "download\|href.*src\|payload\.src" packages/kg-default-nodes/test/nodes/file.test.js packages/koenig-lexical/test/e2e/cards/file-card.test.js -C2

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check the actual FileNode implementation to see what it does
find . -name "*.js" -type f | xargs grep -l "class FileNode\|export.*FileNode" | head -5

Repository: TryGhost/Koenig

Length of output: 283


🏁 Script executed:

# Look at the full file.test.js to see if the exportDOM test is checking for ?download=true
grep -n "download" packages/kg-default-nodes/test/nodes/file.test.js -C3

Repository: TryGhost/Koenig

Length of output: 4378


🏁 Script executed:

# Check if there are multiple file renderers or if the test file and card file diverge
cat packages/kg-default-nodes/test/nodes/file.test.js | grep -A 20 "exportDOM"

Repository: TryGhost/Koenig

Length of output: 4353


🏁 Script executed:

# Look for any recent changes or comments about download parameter
rg -n "download.*true\|download=true" --type=js -C3

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check the file-renderer.js which is used by the Lexical nodes
cat packages/kg-default-nodes/lib/nodes/file/file-renderer.js

Repository: TryGhost/Koenig

Length of output: 6431


🏁 Script executed:

# Compare the two renderers - check file.js from kg-default-cards again
cat packages/kg-default-cards/lib/cards/file.js | sed -n '40,60p'

Repository: TryGhost/Koenig

Length of output: 1444


🏁 Script executed:

# Understand the relationship between these two packages
cat packages/kg-default-cards/package.json | grep -A5 -B5 "kg-default-nodes\|description"
cat packages/kg-default-nodes/package.json | grep -A5 -B5 "kg-default-cards\|description"

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check what version or context the kg-default-cards package is used in
cat packages/kg-default-cards/package.json

Repository: TryGhost/Koenig

Length of output: 1067


🏁 Script executed:

# Look for any tests specifically for the card (not node) version
find . -type f -name "*.test.js" -o -name "*.spec.js" | xargs grep -l "kg-default-cards" 2>/dev/null | head -10

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Verify if there are tests in kg-default-cards itself
ls -la packages/kg-default-cards/test/ 2>/dev/null || echo "No test directory"

Repository: TryGhost/Koenig

Length of output: 388


🏁 Script executed:

# Check if there are references to ?download=true in any actual codebase usage
rg -n "payload\.src.*download\|download.*true" packages/kg-default-cards --type=js

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check git history to understand when ?download=true was added
git log --oneline packages/kg-default-cards/lib/cards/file.js 2>/dev/null | head -10

Repository: TryGhost/Koenig

Length of output: 144


🏁 Script executed:

# Check when the ?download=true was added
git log -p packages/kg-default-cards/lib/cards/file.js 2>/dev/null | grep -A 10 -B 10 "download=true" | head -40

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check if kg-default-cards is actually used/imported elsewhere
rg -n "kg-default-cards" --type=js | grep -E "import|require" | head -20

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Check if there are any other renderers or if kg-default-cards is deprecated
rg -n "from.*kg-default-cards\|require.*kg-default-cards" --type=js

Repository: TryGhost/Koenig

Length of output: 41


🏁 Script executed:

# Verify the actual test for file card in kg-default-cards
cat packages/kg-default-cards/test/cards/file.test.js 2>/dev/null || echo "File not found - checking ls"
ls packages/kg-default-cards/test/cards/ | grep file

Repository: TryGhost/Koenig

Length of output: 87


🏁 Script executed:

# Check the status and usage of kg-default-cards - is it active or deprecated?
cat packages/kg-default-cards/README.md 2>/dev/null || echo "No README"

Repository: TryGhost/Koenig

Length of output: 637


🏁 Script executed:

# See the full git commit that added ?download=true
git log --format="%H %s" packages/kg-default-cards/lib/cards/file.js 2>/dev/null | head -1 | cut -d' ' -f1 | xargs -I {} git show {} --stat 2>/dev/null | head -30

Repository: TryGhost/Koenig

Length of output: 1717


🏁 Script executed:

# Check if kg-default-nodes package is the newer/preferred one
cat packages/kg-default-nodes/README.md 2>/dev/null | head -20

Repository: TryGhost/Koenig

Length of output: 368


🏁 Script executed:

# Confirm: does the kg-default-nodes file-renderer set ?download=true anywhere?
grep -n "download" packages/kg-default-nodes/lib/nodes/file/file-renderer.js

Repository: TryGhost/Koenig

Length of output: 307


Malformed URL when payload.src already contains query parameters.

The suffix is unconditionally appended with ?, so any payload.src containing a query string (e.g., CDN-signed URLs from S3/GCS, or any URL with ?v=...) produces a malformed URL:

https://bucket.s3.amazonaws.com/file.pdf?X-Amz-Signature=abc?download=true
                                                                ^^ invalid

Browsers are lenient but specs-wise this is malformed — the second ? is treated as a literal character in the final query-param value, preventing download from being cleanly parsed. A URL fragment (#anchor) in payload.src has the same problem: the appended ?download=true lands inside the fragment where the server never sees it.

Additionally, escapeHtml should wrap the fully-assembled URL rather than only payload.src for consistency as the code evolves.

🐛 Proposed fix
-            <a class="kg-file-card-container" href="${escapeHtml(payload.src)}?download=true" title="Download" download>
+            <a class="kg-file-card-container" href="${escapeHtml(payload.src.includes('?') ? `${payload.src}&download=true` : `${payload.src}?download=true`)}" title="Download" download>

Or, for clarity, compute it as a local variable before the template literal:

         let html = `
+        `;
+        const downloadSrc = payload.src.includes('?')
+            ? `${payload.src}&download=true`
+            : `${payload.src}?download=true`;
+        html += `
         <div class="kg-card kg-file-card ${classNames}">
-            <a class="kg-file-card-container" href="${escapeHtml(payload.src)}?download=true" title="Download" download>
+            <a class="kg-file-card-container" href="${escapeHtml(downloadSrc)}" title="Download" download>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<a class="kg-file-card-container" href="${escapeHtml(payload.src)}?download=true" title="Download" download>
<a class="kg-file-card-container" href="${escapeHtml(payload.src.includes('?') ? `${payload.src}&download=true` : `${payload.src}?download=true`)}" title="Download" download>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/kg-default-cards/lib/cards/file.js` at line 44, The anchor tag
unconditionally appends "?download=true" to payload.src which breaks URLs that
already contain a query string or fragment; fix by computing a fullDownloadUrl
local variable from payload.src that: 1) strips or preserves fragments properly,
2) appends "?download=true" if there is no existing query string or
"&download=true" if there is one (ensuring the fragment remains after the
query), and then pass the fully-assembled fullDownloadUrl into escapeHtml and
use that escaped value in the href attribute of the kg-file-card-container
anchor; update the code that builds the href (and any title/download attributes)
to use this computed variable instead of escaping payload.src directly.

Copy link
Copy Markdown
Member

@kevinansfield kevinansfield left a comment

Choose a reason for hiding this comment

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

It would be preferable to build the src URL before being used in the template so we can add the query param correctly in cases where files already have query params.

@jloh
Copy link
Copy Markdown
Member Author

jloh commented Mar 9, 2026

We aren't going forward with this in the end, cancelling.

@jloh jloh closed this Mar 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants