-
Notifications
You must be signed in to change notification settings - Fork 29
Development #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Development #4
Conversation
- 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
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
This pull request sets up GitHub code scanning for this repository. Once the scans have completed and the checks have passed, the analysis results for this pull request branch will appear on this overview. Once you merge this pull request, the 'Security' tab will show more code scanning analysis results (for example, for the default branch). Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results. For more information about GitHub code scanning, check out the documentation. |
| 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
on
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 4 days ago
In general, to fix incomplete multi-character sanitization you either (1) switch to a dedicated, well-tested sanitization library, or (2) transform the sanitization so that multi-character patterns cannot reappear after a single pass—e.g., by iterating replacements until the string stabilizes (no further changes) or by replacing with single-character-based patterns. Given the current code and the requirement not to change external behavior drastically, the least intrusive fix is to make the existing sanitization loop until no further replacements are applied.
Concretely, we will refactor sanitizeInput to:
- Perform the same sequence of
.replace(...)operations inside a loop. - Re-run that sequence until the string stops changing (fixed point).
- Preserve the existing trimming and length limiting semantics.
To avoid unnecessary computation, we’ll apply .trim() once at the beginning, then loop over the dangerous-pattern replacements, and finally apply .slice(0, 10000) after the loop. This preserves observable behavior except that inputs which previously might leave behind regenerated "on..." or other multi-pattern combinations will now be fully cleaned. All changes are within src/lib/security.ts in the sanitizeInput function; no new imports or helper methods are required.
-
Copy modified lines R23-R44
| @@ -20,21 +20,28 @@ | ||
| */ | ||
| 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 sanitized = input.trim() | ||
| let previous: string | ||
|
|
||
| // Apply replacements repeatedly until no further changes occur | ||
| do { | ||
| previous = sanitized | ||
| sanitized = sanitized | ||
| // 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, '') | ||
| } while (sanitized !== previous) | ||
|
|
||
| // Limit to reasonable length | ||
| return sanitized.slice(0, 10000) | ||
| } | ||
|
|
||
| /** |
|
There was a problem hiding this 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 PR introduces a comprehensive "Development" update to the Vibe Prompting application, transforming it into a production-ready AI prompt generation platform with significant enhancements to security, architecture, and user experience.
Key Changes:
- Security Hardening: Implements v2 atomic credit system, CSRF protection, session management, rate limiting, and security headers
- Database Migration: Consolidates schema with new v2 security system, request logging, rate limiting tables, and feedback collection
- UI Overhaul: Complete redesign with Neo-Brutalist aesthetic, replacing previous theme with bold borders, shadows, and vibrant colors
- Backend Refactoring: New Edge Functions architecture with fallback mechanisms, improved error handling, and secure credit consumption
Reviewed changes
Copilot reviewed 58 out of 61 changed files in this pull request and generated 20 comments.
Show a summary per file
| File | Description |
|---|---|
| vite.config.ts | Adds production optimizations: Terser minification, console removal, code splitting for vendor bundles, explicit dev/preview ports |
| vercel.json | Implements comprehensive security headers including CSP, X-Frame-Options, and permissions policies |
| tailwind.config.cjs | Extends theme with Neo-Brutalist color palette, custom shadows, animations, and typography |
| supabase/migrations/* | Consolidates and updates database schema with v2 atomic credit system, RLS policies, and helper functions |
| supabase/functions/* | New get-provider-info endpoint and completely rewritten generate-prompt with validation and credit checks |
| src/pages/* | Redesigned pages with Neo-Brutalist styling; adds new DashboardPage, ExplorePage, PricingPage; replaces GeneratePromptPage |
| src/lib/* | New security utilities (CSRF, session management), refactored Supabase client, API layer with fallback logic |
| src/hooks/* | Replaces useCredits with useCreditsSecure for read-only credit fetching with real-time updates |
| src/components/* | Updated Navbar with credit display and feedback modal; redesigned UI components with Neo-Brutalist theme |
| src/styles/index.css | Complete CSS overhaul with Neo-Brutalist design system, custom utility classes, and Google Fonts integration |
| package.json | Updates Vite to v7.2.2 and adds Terser for minification |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // Check honeypot field using native form elements | ||
| const form = document.querySelector('form') | ||
| const honeypot = form?.querySelector('input[name="honeypot_website"]') as HTMLInputElement | ||
| if (honeypot?.value) { | ||
| console.warn('Bot detected - honeypot field filled') | ||
| return | ||
| } |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The honeypot field check uses DOM manipulation which is fragile and can be bypassed. The field is checked using document.querySelector('form') which could select the wrong form if multiple forms exist on the page. Instead, use React refs or pass the honeypot value through the form's controlled state to ensure you're checking the correct field value.
| minify: 'terser', | ||
| terserOptions: { | ||
| compress: { | ||
| drop_console: mode === 'production', // Remove console.log in production |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The condition mode === 'production' for dropping console statements should be more specific. This will only work if you explicitly set NODE_ENV or pass --mode production. Consider also checking for 'build' mode or using process.env.NODE_ENV === 'production' to ensure console logs are consistently removed in production builds.
| }, | ||
| { | ||
| "key": "Content-Security-Policy", | ||
| "value": "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://va.vercel-scripts.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; connect-src 'self' https://*.supabase.co https://generativelanguage.googleapis.com https://openrouter.ai https://va.vercel-scripts.com; frame-ancestors 'none';" |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CSP 'unsafe-inline' and 'unsafe-eval' directives for script-src significantly weaken the Content Security Policy and expose the application to XSS attacks. Consider using nonces or hashes for inline scripts instead, and avoid eval-based operations. If these are truly necessary, document why they're required and what mitigations are in place.
| CREATE POLICY "profiles_update_policy" | ||
| ON profiles FOR UPDATE | ||
| USING (auth.uid() = id) | ||
| WITH CHECK ( | ||
| auth.uid() = id | ||
| AND ( | ||
| -- Allow if credits haven't changed | ||
| credits = (SELECT credits FROM profiles WHERE id = auth.uid()) | ||
| -- Or if service role (for RPC functions) | ||
| OR auth.jwt()->>'role' = 'service_role' | ||
| ) | ||
| ); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The RLS policy for profiles_update_policy allows users to bypass credit deduction constraints by checking auth.jwt()->>'role' = 'service_role'. However, this JWT claim can potentially be manipulated on the client side. The service role check should only be used server-side with proper authentication. This could allow users to grant themselves unlimited credits.
| CREATE TABLE IF NOT EXISTS public.profiles ( | ||
| id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE, | ||
| username TEXT UNIQUE, | ||
| full_name TEXT, | ||
| avatar_url TEXT, | ||
| bio TEXT, | ||
| created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), | ||
| updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() | ||
| ); |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing email field in the profiles table schema. Line 139 in handle_new_user() references NEW.email when inserting into profiles, but the CREATE TABLE statement for profiles (lines 12-20) doesn't include an email column. This will cause the trigger to fail when new users sign up.
| 'General Development': Code, | ||
| } | ||
|
|
||
| const CATEGORY_COLORS: Record<string, string> = { |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable CATEGORY_COLORS.
|
|
||
| export default function ExplorePage() { | ||
| const { theme } = useTheme() | ||
| const { user } = useAuth() |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable user.
| export default function ExplorePage() { | ||
| const { theme } = useTheme() | ||
| const { user } = useAuth() | ||
| const navigate = useNavigate() |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable navigate.
| import { useAuth } from '@/context/AuthContext' | ||
| import { useNavigate } from 'react-router-dom' | ||
| import { useCredits } from '@/hooks/useCredits' | ||
| import { useCredits } from '@/hooks/useCreditsSecure' |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import useCredits.
| 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; | ||
|
|
||
| -- Log the transaction | ||
| INSERT INTO request_log (user_id, request_id, action, credits_consumed, metadata) | ||
| VALUES (p_user_id, p_request_id, p_action, p_credits_to_consume, p_metadata); | ||
|
|
||
| -- Return success | ||
| RETURN jsonb_build_object( | ||
| 'success', true, | ||
| 'credits_consumed', p_credits_to_consume, | ||
| 'credits_remaining', v_new_credits, | ||
| 'tier', v_tier | ||
| ); | ||
| END; | ||
| $$; |
Copilot
AI
Jan 6, 2026
There was a problem hiding this comment.
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 defined as SECURITY DEFINER and takes a caller-supplied p_user_id and p_credits_to_consume without verifying that p_user_id matches auth.uid() or that the credit amount is positive. Because this function runs with elevated privileges and is callable by regular authenticated clients (see the GRANT below), an attacker who knows another user's UUID can deduct or even add credits to any account by passing another user's ID or a negative p_credits_to_consume. This breaks tenant isolation for the credit system; restrict this function to operating only on auth.uid() and enforce a strictly positive credit amount before updating profiles.credits.




No description provided.