Skip to content

✨ Add useCardFetch hook for custom card data fetching#4230

Merged
clubanderson merged 2 commits intomainfrom
fix/4205-card-fetch
Apr 2, 2026
Merged

✨ Add useCardFetch hook for custom card data fetching#4230
clubanderson merged 2 commits intomainfrom
fix/4205-card-fetch

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

  • Adds useCardFetch(url, options) hook to the Tier 2 custom card sandbox, enabling cards to fetch data from external API endpoints
  • New Go backend proxy at /api/card-proxy with SSRF protection (private IP blocking, URL validation, response size limits)
  • fetch/XMLHttpRequest remain blocked in the sandbox — useCardFetch is the only sanctioned way for card code to retrieve external data

Usage in custom card code

export default function MyCard() {
  const { data, loading, error } = useCardFetch('https://api.example.com/metrics')
  
  if (loading) return <SpinWrapper />
  if (error) return <div className="text-red-400">{error}</div>
  
  return <div>{JSON.stringify(data)}</div>
}

Security

Layer Protection
URL validation Only http/https, max 2048 chars
SSRF protection DNS resolution + private IP range blocking (RFC 1918, loopback, link-local)
Response limits 5 MB max response body
Timeout 15s request timeout, no redirect following
Concurrency Max 5 concurrent fetches per card
Sandbox fetch/XMLHttpRequest still blocked — only useCardFetch available

Test plan

  • Go unit tests for SSRF IP blocking (TestIsBlockedIP)
  • Frontend build passes
  • Go build passes
  • Manual test: create Tier 2 card using useCardFetch to fetch from a public API
  • Manual test: verify blocked URLs (localhost, 10.x, 192.168.x) return 403

Fixes #4205

Custom Tier 2 cards can now fetch external API data via useCardFetch(),
which routes requests through a backend proxy at /api/card-proxy.

Backend (Go):
- New CardProxyHandler with SSRF protection (private IP blocking)
- URL validation, 5 MB response limit, 15s timeout
- No redirects followed, GET-only

Frontend (TypeScript):
- useCardFetch<T>(url, options) hook injected into sandbox scope
- Returns { data, loading, error, refetch }
- Auto-refresh support with 5s minimum interval
- Max 5 concurrent fetches per card, cleanup on unmount

fetch/XMLHttpRequest remain blocked in the sandbox — useCardFetch is
the only sanctioned way for card code to retrieve external data.

Closes #4205

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings April 2, 2026 13:02
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 2, 2026
@netlify
Copy link
Copy Markdown

netlify bot commented Apr 2, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit 12cea3e
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69ce6b897b131a00087c3bb3
😎 Deploy Preview https://deploy-preview-4230.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@kubestellar-prow kubestellar-prow bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 2, 2026
@clubanderson
Copy link
Copy Markdown
Collaborator Author

/lgtm
/approve

@kubestellar-prow
Copy link
Copy Markdown
Contributor

@clubanderson: you cannot LGTM your own PR.

Details

In response to this:

/lgtm
/approve

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@kubestellar-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: clubanderson

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@kubestellar-prow kubestellar-prow bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Apr 2, 2026
1. Move IP blocking into custom DialContext on cardProxyClient transport,
   preventing DNS rebinding / TOCTOU bypass (resolve + check at connect time)
2. Refactor useCardFetch to createCardFetchScope() factory — each card gets
   its own concurrency counter, so unmounting one card doesn't corrupt another
3. Initialize loading=true when URL is provided (fix brief false-idle flash)
4. Fix stale comment: blockedIPPrefixes → blockedCIDRs

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
@clubanderson clubanderson merged commit 95b17e4 into main Apr 2, 2026
19 of 20 checks passed
@kubestellar-prow kubestellar-prow bot deleted the fix/4205-card-fetch branch April 2, 2026 13:18
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 2, 2026

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

clubanderson added a commit that referenced this pull request Apr 2, 2026
Follow-up to #4230 addressing code review and silent-failure findings:

Backend (card_proxy.go):
- CIDR parse failure now log.Fatalf instead of silent skip
- Use http.NewRequestWithContext for client disconnect cancellation
- Detect 3xx redirects and return helpful error instead of opaque status
- Add logging on all error paths + audit log on success
- Log response size on oversized body rejection

Frontend (useCardFetch.ts):
- Add AbortController to cancel in-flight fetches on URL change/unmount
- Guard localStorage.getItem against SecurityError in sandboxed iframes
- Set loading=false when concurrency limit is hit
- Wrap res.json() with helpful error for non-JSON 200 responses

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
clubanderson added a commit that referenced this pull request Apr 2, 2026
…#4237)

Follow-up to #4230 addressing code review and silent-failure findings:

Backend (card_proxy.go):
- CIDR parse failure now log.Fatalf instead of silent skip
- Use http.NewRequestWithContext for client disconnect cancellation
- Detect 3xx redirects and return helpful error instead of opaque status
- Add logging on all error paths + audit log on success
- Log response size on oversized body rejection

Frontend (useCardFetch.ts):
- Add AbortController to cancel in-flight fetches on URL change/unmount
- Guard localStorage.getItem against SecurityError in sandboxed iframes
- Set loading=false when concurrency limit is hit
- Wrap res.json() with helpful error for non-JSON 200 responses

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
@clubanderson clubanderson review requested due to automatic review settings April 2, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature Request: custom code cards still can't fetch data

1 participant