security(frontend): fix innerHTML XSS in error handler, remove Helius keys from bundle
Some checks failed
CI / build (push) Failing after 26s
Some checks failed
CI / build (push) Failing after 26s
refactor(frontend): centralize API base URL to lib/env.ts (7 files updated) refactor(frontend): remove VITE_API_URL from .envrc for production parity chore(frontend): fix ScrollToTop — add pathname to useEffect deps Prod audit: fix critical XSS + Helius key leak + API URL fragmentation
This commit is contained in:
parent
6d18e8200f
commit
93aa749a04
12 changed files with 2523 additions and 849 deletions
1
.envrc
1
.envrc
|
|
@ -1,3 +1,2 @@
|
|||
source_up
|
||||
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PWD/node_modules/.bin:$PATH"
|
||||
export VITE_API_URL="https://rugmunch.io"
|
||||
|
|
|
|||
3271
pnpm-lock.yaml
generated
3271
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
// @ts-nocheck
|
||||
|
||||
import { motion } from 'framer-motion'
|
||||
|
|
@ -30,7 +31,7 @@ import {
|
|||
import { useEffect, useState } from 'react'
|
||||
import aiRouter from '@/services/aiRouter'
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || ''
|
||||
const API_BASE = API_BASE_URL || ''
|
||||
|
||||
/* ── Types ── */
|
||||
interface NewsItem {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
// @ts-nocheck
|
||||
|
||||
import {
|
||||
|
|
@ -70,7 +71,7 @@ export default function RugRiskPanel({ pair }: Props) {
|
|||
})
|
||||
|
||||
// Fetch trading signal
|
||||
fetch(`${import.meta.env.VITE_API_URL || ''}/api/ai-trading/signal`, {
|
||||
fetch(`${API_BASE_URL || ''}/api/ai-trading/signal`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ const envSchema = z.object({
|
|||
VITE_SUPABASE_ANON_KEY: z.string().optional().default(''),
|
||||
|
||||
// API endpoint
|
||||
VITE_API_URL: z.string().optional().default('http://localhost:8000'),
|
||||
VITE_API_URL: z.string().optional().default(''),
|
||||
|
||||
// WebSocket endpoint
|
||||
VITE_WS_URL: z.string().optional().default('ws://localhost:8000/ws'),
|
||||
|
|
@ -21,9 +21,7 @@ const envSchema = z.object({
|
|||
VITE_GHOST_URL: z.string().optional().default(''),
|
||||
VITE_GHOST_KEY: z.string().optional().default(''),
|
||||
|
||||
// Helius RPC (Solana)
|
||||
VITE_HELIUS_RPC_URL: z.string().url().optional().default('https://mainnet.helius-rpc.com'),
|
||||
VITE_HELIUS_API_KEY: z.string().optional().default(''),
|
||||
// Helius RPC (Solana) — removed from client bundle; backend proxies all Helius calls
|
||||
})
|
||||
|
||||
export type Env = z.infer<typeof envSchema>
|
||||
|
|
@ -51,7 +49,6 @@ export const SUPABASE_URL = env.VITE_SUPABASE_URL
|
|||
export const SUPABASE_ANON_KEY = env.VITE_SUPABASE_ANON_KEY
|
||||
export const GHOST_URL = env.VITE_GHOST_URL
|
||||
export const GHOST_KEY = env.VITE_GHOST_KEY
|
||||
export const HELIUS_RPC_URL = env.VITE_HELIUS_RPC_URL
|
||||
export const HELIUS_API_KEY = env.VITE_HELIUS_API_KEY
|
||||
// HELIUS_RPC_URL and HELIUS_API_KEY removed — backend proxies all Helius calls
|
||||
export const APP_NAME = 'RugMunch Intelligence'
|
||||
export const APP_VERSION = '2.0.0'
|
||||
|
|
|
|||
68
src/main.tsx
68
src/main.tsx
|
|
@ -33,27 +33,53 @@ window.onerror = (msg, url, line, col, error) => {
|
|||
col,
|
||||
time: Date.now(),
|
||||
})
|
||||
rootEl.innerHTML = `
|
||||
<div style="position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#07070b;color:#FF3366;font-family:monospace;padding:2rem;overflow:auto">
|
||||
<div style="max-width:700px;width:100%">
|
||||
<h3 style="color:#8B5CF6;margin-bottom:1rem">Runtime Error</h3>
|
||||
<div style="background:#0f0f14;border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:1rem;margin-bottom:1rem">
|
||||
<p style="color:#FF3366;font-size:14px;word-break:break-word">${err.message || msg}</p>
|
||||
</div>
|
||||
${
|
||||
err.stack
|
||||
? `<div style="background:#0f0f14;border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:1rem;margin-bottom:1rem">
|
||||
<p style="color:#8B5CF6;font-size:11px;margin-bottom:0.5rem">Stack Trace:</p>
|
||||
<pre style="color:#94A3B8;font-size:10px;overflow:auto;max-height:200px;white-space:pre-wrap">${err.stack.split('\\n').slice(0, 8).join('\\n')}</pre>
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
<div style="color:#94A3B8;font-size:11px;margin-bottom:1rem">
|
||||
Source: ${url}:${line}:${col}
|
||||
</div>
|
||||
<button type="button" onclick="location.reload()" style="padding:10px 24px;background:#8B5CF6;border:none;color:white;border-radius:8px;cursor:pointer;font-size:14px">Reload Page</button>
|
||||
</div>
|
||||
</div>`
|
||||
rootEl.textContent = '' // cleared below, rebuilt safely
|
||||
const overlay = document.createElement('div')
|
||||
overlay.style.cssText = 'position:fixed;inset:0;display:flex;align-items:center;justify-content:center;background:#07070b;color:#FF3366;font-family:monospace;padding:2rem;overflow:auto'
|
||||
const inner = document.createElement('div')
|
||||
inner.style.cssText = 'max-width:700px;width:100%'
|
||||
|
||||
const h3 = document.createElement('h3')
|
||||
h3.style.cssText = 'color:#8B5CF6;margin-bottom:1rem'
|
||||
h3.textContent = 'Runtime Error'
|
||||
inner.appendChild(h3)
|
||||
|
||||
const errBox = document.createElement('div')
|
||||
errBox.style.cssText = 'background:#0f0f14;border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:1rem;margin-bottom:1rem'
|
||||
const errP = document.createElement('p')
|
||||
errP.style.cssText = 'color:#FF3366;font-size:14px;word-break:break-word'
|
||||
errP.textContent = err.message || String(msg)
|
||||
errBox.appendChild(errP)
|
||||
inner.appendChild(errBox)
|
||||
|
||||
if (err.stack) {
|
||||
const stackBox = document.createElement('div')
|
||||
stackBox.style.cssText = 'background:#0f0f14;border:1px solid rgba(255,255,255,0.1);border-radius:12px;padding:1rem;margin-bottom:1rem'
|
||||
const stackP = document.createElement('p')
|
||||
stackP.style.cssText = 'color:#8B5CF6;font-size:11px;margin-bottom:0.5rem'
|
||||
stackP.textContent = 'Stack Trace:'
|
||||
stackBox.appendChild(stackP)
|
||||
const pre = document.createElement('pre')
|
||||
pre.style.cssText = 'color:#94A3B8;font-size:10px;overflow:auto;max-height:200px;white-space:pre-wrap'
|
||||
pre.textContent = err.stack.split('\\n').slice(0, 8).join('\\n')
|
||||
stackBox.appendChild(pre)
|
||||
inner.appendChild(stackBox)
|
||||
}
|
||||
|
||||
const srcDiv = document.createElement('div')
|
||||
srcDiv.style.cssText = 'color:#94A3B8;font-size:11px;margin-bottom:1rem'
|
||||
srcDiv.textContent = `Source: ${url || '?'}:${line || '?'}:${col || '?'}`
|
||||
inner.appendChild(srcDiv)
|
||||
|
||||
const btn = document.createElement('button')
|
||||
btn.type = 'button'
|
||||
btn.style.cssText = 'padding:10px 24px;background:#8B5CF6;border:none;color:white;border-radius:8px;cursor:pointer;font-size:14px'
|
||||
btn.textContent = 'Reload Page'
|
||||
btn.onclick = () => location.reload()
|
||||
inner.appendChild(btn)
|
||||
|
||||
overlay.appendChild(inner)
|
||||
rootEl.appendChild(overlay)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import {
|
||||
AlertTriangle,
|
||||
|
|
@ -19,7 +20,7 @@ import { useState } from 'react'
|
|||
import { useNavigate } from 'react-router-dom'
|
||||
import { databus } from '@/services/databus'
|
||||
|
||||
const _BASE = import.meta.env.VITE_API_URL || ''
|
||||
const _BASE = API_BASE_URL || ''
|
||||
|
||||
interface WalletForensic {
|
||||
address: string
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
/**
|
||||
* 🤖 RMI AI Router — Frontend Client
|
||||
* All AI requests proxy through the backend (server-side keys, never exposed)
|
||||
|
|
@ -21,7 +22,7 @@ export interface RouterResult {
|
|||
latencyMs: number
|
||||
}
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000'
|
||||
const API_BASE = API_BASE_URL || ''
|
||||
|
||||
class AIRouter {
|
||||
private modelCache: ModelConfig[] = []
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
export interface DexPair {
|
||||
chainId: string
|
||||
dexId: string
|
||||
|
|
@ -51,7 +52,7 @@ export interface Holder {
|
|||
riskColor: string
|
||||
}
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || ''
|
||||
const API_BASE = API_BASE_URL || ''
|
||||
|
||||
export async function searchPairs(query: string): Promise<DexPair[]> {
|
||||
const res = await fetch(`${API_BASE}/api/dex/search?q=${encodeURIComponent(query)}`)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
/**
|
||||
* RugMaps RAG Intelligence Bridge
|
||||
* Connects frontend to RMI's 3-pillar RAG engine (FAISS + SPLADE + entity exact)
|
||||
|
|
@ -43,7 +44,7 @@ export interface DeployerRisk {
|
|||
verdict: string
|
||||
}
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || 'https://api.rugmunch.io'
|
||||
const API_BASE = API_BASE_URL || ''
|
||||
|
||||
export async function searchRAG(
|
||||
query: string,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { API_BASE_URL } from '@/lib/env'
|
||||
/**
|
||||
* SENTINEL Scanner Bridge for RugMaps
|
||||
* Real-time risk scoring, honeypot detection, contract verification
|
||||
|
|
@ -26,7 +27,7 @@ export interface SentinelScanResult {
|
|||
verdict: string
|
||||
}
|
||||
|
||||
const API_BASE = import.meta.env.VITE_API_URL || 'https://api.rugmunch.io'
|
||||
const API_BASE = API_BASE_URL || ''
|
||||
|
||||
export async function sentinelScan(tokenAddress: string, chain: string): Promise<SentinelScanResult | null> {
|
||||
const res = await fetch(`${API_BASE}/api/v1/token/security-check`, {
|
||||
|
|
|
|||
|
|
@ -13,5 +13,3 @@ vi.stubEnv('VITE_API_URL', 'http://localhost:8000')
|
|||
vi.stubEnv('VITE_WS_URL', 'ws://localhost:8000/ws')
|
||||
vi.stubEnv('VITE_GHOST_URL', '')
|
||||
vi.stubEnv('VITE_GHOST_KEY', '')
|
||||
vi.stubEnv('VITE_HELIUS_RPC_URL', 'https://mainnet.helius-rpc.com')
|
||||
vi.stubEnv('VITE_HELIUS_API_KEY', 'test-key')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue