Skip to content

Conversation

@Addy-shetty
Copy link
Owner

No description provided.

- Create PricingPage with 3 tier plans (Starter/Pro/Ultimate)
- Add animated pricing cards with hover effects
- Include FAQ section and trust indicators
- Add CreditDisplay component in navbar
- Show credit count with low credit warning
- Add pricing link to navigation
- Match existing app theme and animations
- Responsive design for all devices
- Add refundCredit function to useCredits hook
- Add decrementAnonymousGeneration for anonymous users
- Create refund_credit database function
- Wrap generation in try-catch to refund on failure
- Show toast notification when credit is refunded
- Fix CreditDisplay to use credits.creditsRemaining
- Ensure credits only deducted on successful generation
- **Type:** 🐛 Bug
- **Severity:** Medium
- **Component:** `PromptCard.tsx` / `HistoryGrid.tsx`
- **Description:** In the prompt history grid, cards with medium-length text are pushing the action toolbar (icons) out of the viewport. The bottom border cuts off the buttons.
- **Expected Behavior:** The footer buttons should always be visible. Long text should either be truncated with ellipses (`...`) or the card should expand vertically to fit the content.

The Prompt: 

# Role

- Act as a Senior Frontend Developer expert in React and Tailwind CSS.
- 
- # Context
- I have a UI bug in my \`PromptCard\` component (or the component used to display prompt history).
- - \*\*Current Behavior:\*\* When the prompt description text is too long, it pushes the bottom action buttons (Copy, Edit, Delete) out of the view, causing them to be clipped or hidden by the card's overflow.
- - \*\*Visual Reference:\*\* The card has a fixed height or a grid constraint that isn't adapting to the content size.
- 
- # The Task
- Please refactor the CSS/Tailwind classes for this card to ensure the footer stays pinned and visible.
- 
- 1. \*\*Flexbox Structure:\*\* Ensure the card container uses \`flex flex-col h-full justify-between\`.
- 2. \*\*Text Truncation:\*\* Apply Tailwind's \`line-clamp\` utility to the description text area so it doesn't expand infinitely.
-    - Use \`line-clamp-4\` (or similar) for the body text.
-    - Add \`overflow-hidden\` to the text container to handle spillover gracefully.
- 3. \*\*Spacing:\*\* Verify \`p-4\` or \`p-6\` padding is consistent and doesn't conflict with the footer alignment.
- 
- # Example of Desired Output (Tailwind)
- \`\`\`tsx
- <div className="flex flex-col h-full bg-card border rounded-xl p-4 shadow-sm hover:shadow-md transition-all">
-   {/\* Header \*/}
-   <div className="mb-2">...</div>
-   
-   {/\* Body - THIS IS THE FIX \*/}
-   <div className="flex-grow overflow-hidden">
-     <p className="text-sm text-muted-foreground line-clamp-4">
-       {promptText}
-     </p>
-   </div>
- 
-   {/\* Footer - Should always be visible \*/}
-   <div className="flex items-center justify-end gap-2 mt-4 pt-2 border-t">
-     <Button variant="ghost" size="icon">...</Button>
-     <Button variant="ghost" size="icon">...</Button>
-     <Button variant="ghost" size="icon" className="text-destructive">...</Button>
-   </div>
- </div>
Copilot AI review requested due to automatic review settings January 6, 2026 09:56
@vercel
Copy link

vercel bot commented Jan 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
vibe-prompting Ready Ready Preview, Comment Jan 6, 2026 9:56am

@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 6, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
7 Security Hotspots
6.4% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Comment on lines 24 to +33
return input
.trim()
.replace(/[<>]/g, '') // Remove < and >
.replace(/javascript:/gi, '') // Remove javascript: protocol
.replace(/on\w+\s*=/gi, '') // Remove event handlers
// Remove HTML tags
.replace(/<\/?[^>]+(>|$)/g, '')
// Remove javascript: protocol
.replace(/javascript:/gi, '')
// Remove data: protocol (can be used for XSS)
.replace(/data:text\/html/gi, '')
// Remove event handlers
.replace(/on\w+\s*=/gi, '')

Check failure

Code scanning / CodeQL

Incomplete multi-character sanitization High

This string may still contain
on
, which may cause an HTML attribute injection vulnerability.

Copilot Autofix

AI 3 days ago

In general, to fix incomplete multi‑character sanitization, you either (a) use a well‑tested sanitization library or (b) ensure that any regex that removes multi‑character patterns is applied until the string no longer changes, so partially removed sequences cannot re‑combine into new dangerous patterns.

For this code, the least intrusive, behavior‑preserving fix is to wrap the existing chained .replace(...).slice(...) logic in a loop that repeats the entire sanitization pipeline until the output stops changing. This avoids having to reason about each individual regex and ensures that if any step ever creates a new on\w+\s*= (or similar multi‑character pattern) from previously safe content, it will be removed on the next iteration. We keep the existing functionality intact: same regexes, same maximum length, same trimming; the only difference is that we reapply them until the string reaches a fixed point.

Concretely, in src/lib/security.ts, within sanitizeInput, we will:

  • Introduce a let sanitized = input; let previous: string; pair.
  • Run a do { ... } while (sanitized !== previous) loop.
  • Inside the loop, apply the current chain starting from previous.trim() and assigning the result to sanitized.
  • Return sanitized at the end.

We will not change other functions or imports, and we will not introduce new dependencies.

Suggested changeset 1
src/lib/security.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/lib/security.ts b/src/lib/security.ts
--- a/src/lib/security.ts
+++ b/src/lib/security.ts
@@ -20,21 +20,29 @@
  */
 export function sanitizeInput(input: string): string {
   if (!input || typeof input !== 'string') return ''
-  
-  return input
-    .trim()
-    // Remove HTML tags
-    .replace(/<\/?[^>]+(>|$)/g, '')
-    // Remove javascript: protocol
-    .replace(/javascript:/gi, '')
-    // Remove data: protocol (can be used for XSS)
-    .replace(/data:text\/html/gi, '')
-    // Remove event handlers
-    .replace(/on\w+\s*=/gi, '')
-    // Remove null bytes
-    .replace(/\0/g, '')
-    // Limit to reasonable length
-    .slice(0, 10000)
+
+  let previous: string
+  let sanitized = input
+
+  do {
+    previous = sanitized
+    sanitized = previous
+      .trim()
+      // Remove HTML tags
+      .replace(/<\/?[^>]+(>|$)/g, '')
+      // Remove javascript: protocol
+      .replace(/javascript:/gi, '')
+      // Remove data: protocol (can be used for XSS)
+      .replace(/data:text\/html/gi, '')
+      // Remove event handlers
+      .replace(/on\w+\s*=/gi, '')
+      // Remove null bytes
+      .replace(/\0/g, '')
+      // Limit to reasonable length
+      .slice(0, 10000)
+  } while (sanitized !== previous)
+
+  return sanitized
 }
 
 /**
EOF
@@ -20,21 +20,29 @@
*/
export function sanitizeInput(input: string): string {
if (!input || typeof input !== 'string') return ''

return input
.trim()
// Remove HTML tags
.replace(/<\/?[^>]+(>|$)/g, '')
// Remove javascript: protocol
.replace(/javascript:/gi, '')
// Remove data: protocol (can be used for XSS)
.replace(/data:text\/html/gi, '')
// Remove event handlers
.replace(/on\w+\s*=/gi, '')
// Remove null bytes
.replace(/\0/g, '')
// Limit to reasonable length
.slice(0, 10000)

let previous: string
let sanitized = input

do {
previous = sanitized
sanitized = previous
.trim()
// Remove HTML tags
.replace(/<\/?[^>]+(>|$)/g, '')
// Remove javascript: protocol
.replace(/javascript:/gi, '')
// Remove data: protocol (can be used for XSS)
.replace(/data:text\/html/gi, '')
// Remove event handlers
.replace(/on\w+\s*=/gi, '')
// Remove null bytes
.replace(/\0/g, '')
// Limit to reasonable length
.slice(0, 10000)
} while (sanitized !== previous)

return sanitized
}

/**
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request implements a major UI/UX redesign with a Neo-Brutalist theme and introduces several new features including a secure credit system, pricing page, dashboard, and enhanced security measures. The changes involve significant refactoring of the authentication flow, database schema, and prompt generation system.

Key changes:

  • Neo-Brutalist UI theme with bold borders, shadows, and vibrant colors
  • Secure credit system with atomic transactions and rate limiting
  • New pages: Dashboard, Pricing, Explore, and secure prompt generation
  • Enhanced security: CSRF protection, input sanitization, session management, CSP headers

Reviewed changes

Copilot reviewed 58 out of 61 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
vite.config.ts Added production optimizations: code splitting, console removal, terser minification
vercel.json Implemented security headers (CSP, X-Frame-Options, HSTS-like policies)
tailwind.config.cjs Complete Neo-Brutalist theme configuration with custom colors, shadows, animations
supabase/migrations/* New secure credit system with RLS policies, rate limiting, and feedback table
supabase/functions/generate-prompt Complete rewrite with authentication, rate limiting, and credit consumption
src/lib/security.ts Added CSRF token management and enhanced input sanitization
src/lib/sessionManager.ts New session timeout monitoring with activity tracking
src/lib/api.ts New API client with retry logic and fallback mechanisms
src/hooks/useCreditsSecure.ts Secure credit fetching with RPC fallback
src/pages/* Major UI redesign across all pages with Neo-Brutalist styling
src/components/* Updated Navbar, new CreditDisplay, enhanced UI components

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +12 to +28
errorInfo: ErrorInfo | null
}

export class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
error: null,
errorInfo: null,
}

public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error, errorInfo: null }
}

public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo)
this.setState({ errorInfo })
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Component state property 'errorInfo' is written, but it is never read.
Component state property 'errorInfo' is written, but it is never read.
Component state property 'errorInfo' is written, but it is never read.

Suggested change
errorInfo: ErrorInfo | null
}
export class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
error: null,
errorInfo: null,
}
public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error, errorInfo: null }
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo)
this.setState({ errorInfo })
}
export class ErrorBoundary extends Component<Props, State> {
public state: State = {
hasError: false,
error: null,
}
public static getDerivedStateFromError(error: Error): State {
return { hasError: true, error }
}
public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('ErrorBoundary caught an error:', error, errorInfo)

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +17
Users,
CheckCircle2
} from 'lucide-react'
import { useTheme } from '@/context/ThemeContext'
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused import CheckCircle2.

Suggested change
Users,
CheckCircle2
} from 'lucide-react'
import { useTheme } from '@/context/ThemeContext'
Users
} from 'lucide-react'
import { useTheme } from '@/context/ThemeContext'
import { useTheme } from '@/context/ThemeContext'

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +30
const [mounted, setMounted] = useState(false)
const [demoStep, setDemoStep] = useState(0)
const [statsCounter, setStatsCounter] = useState({ prompts: 0, users: 0, satisfaction: 0 })

useEffect(() => {
setMounted(true)

Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable mounted.

Suggested change
const [mounted, setMounted] = useState(false)
const [demoStep, setDemoStep] = useState(0)
const [statsCounter, setStatsCounter] = useState({ prompts: 0, users: 0, satisfaction: 0 })
useEffect(() => {
setMounted(true)
const [demoStep, setDemoStep] = useState(0)
const [statsCounter, setStatsCounter] = useState({ prompts: 0, users: 0, satisfaction: 0 })
useEffect(() => {

Copilot uses AI. Check for mistakes.
import { supabase } from './supabase'

// Retry configuration
const MAX_RETRIES = 3
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable MAX_RETRIES.

Copilot uses AI. Check for mistakes.

// Retry configuration
const MAX_RETRIES = 3
const INITIAL_RETRY_DELAY = 1000 // 1 second
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable INITIAL_RETRY_DELAY.

Copilot uses AI. Check for mistakes.
'General Development': Code,
}

const CATEGORY_COLORS: Record<string, string> = {
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable CATEGORY_COLORS.

Copilot uses AI. Check for mistakes.

export default function ExplorePage() {
const { theme } = useTheme()
const { user } = useAuth()
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable user.

Copilot uses AI. Check for mistakes.
export default function ExplorePage() {
const { theme } = useTheme()
const { user } = useAuth()
const navigate = useNavigate()
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused variable navigate.

Copilot uses AI. Check for mistakes.
import { useAuth } from '@/context/AuthContext'
import { useNavigate } from 'react-router-dom'
import { useCredits } from '@/hooks/useCredits'
import { useCredits } from '@/hooks/useCreditsSecure'
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

Unused import useCredits.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +161
CREATE OR REPLACE FUNCTION consume_user_credits(
p_user_id UUID,
p_credits_to_consume INTEGER,
p_request_id TEXT,
p_action TEXT DEFAULT 'generate_prompt',
p_metadata JSONB DEFAULT '{}'
)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
v_current_credits INTEGER;
v_new_credits INTEGER;
v_tier TEXT;
BEGIN
-- Check for duplicate request (idempotency)
IF EXISTS (SELECT 1 FROM request_log WHERE request_id = p_request_id) THEN
RETURN jsonb_build_object(
'success', false,
'error', 'duplicate_request',
'message', 'This request has already been processed'
);
END IF;

-- Lock the user's row and get current credits
SELECT credits, tier INTO v_current_credits, v_tier
FROM profiles
WHERE id = p_user_id
FOR UPDATE; -- Critical: prevents race conditions

-- Check if user exists
IF NOT FOUND THEN
RETURN jsonb_build_object(
'success', false,
'error', 'user_not_found',
'message', 'User profile not found'
);
END IF;

-- Check sufficient credits
IF v_current_credits < p_credits_to_consume THEN
RETURN jsonb_build_object(
'success', false,
'error', 'insufficient_credits',
'message', 'Not enough credits',
'current_credits', v_current_credits,
'required_credits', p_credits_to_consume
);
END IF;

-- Deduct credits
v_new_credits := v_current_credits - p_credits_to_consume;

UPDATE profiles
SET credits = v_new_credits,
updated_at = NOW()
WHERE id = p_user_id;
Copy link

Copilot AI Jan 6, 2026

Choose a reason for hiding this comment

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

The consume_user_credits function is declared SECURITY DEFINER and later granted EXECUTE to the authenticated role, but it fully trusts the caller-supplied p_user_id and p_credits_to_consume parameters without checking auth.uid() or enforcing that the credit delta is positive. An attacker with an authenticated Supabase session can call this RPC directly via /rest/v1/rpc/consume_user_credits using arbitrary p_user_id (including other users) and even negative p_credits_to_consume values, allowing them to arbitrarily change credits (e.g., inflate their own balance) while bypassing the intended Edge Function and business logic. To fix this, tightly bind operations to the caller by validating p_user_id = auth.uid(), enforcing p_credits_to_consume > 0, and/or restricting EXECUTE on this SECURITY DEFINER function to a server-side role only.

Copilot uses AI. Check for mistakes.
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