Skip to content

Comments

Feature/brand naming exploration#4

Open
filippofilip95 wants to merge 3 commits intomasterfrom
feature/brand-naming-exploration
Open

Feature/brand naming exploration#4
filippofilip95 wants to merge 3 commits intomasterfrom
feature/brand-naming-exploration

Conversation

@filippofilip95
Copy link
Member

No description provided.

filippofilip95 and others added 2 commits December 22, 2025 21:25
Features implemented:
- One-click email approve/reject with magic links
- Mobile-first review page with fixed bottom action bar
- Slack interactive buttons with signature verification
- Dashboard search/filter and review templates
- Toast notification system (Radix-based)
- Content format support (PLAIN_TEXT, MARKDOWN, HTML)
- ContentRenderer component with DOMPurify sanitization

Security improvements (50 issues fixed across 3 code reviews):
- HTML sanitization with DOMPurify
- Slack webhook HMAC-SHA256 signature verification
- Race conditions fixed with Prisma transactions
- XSS prevention in markdown link rendering
- Input validation and sanitization throughout

Accessibility improvements:
- ARIA labels on all interactive elements
- Role attributes for status indicators and dialogs
- Full i18n support with next-intl

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Dec 22, 2025

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

Project Deployment Review Updated (UTC)
sendvelo Error Error Dec 22, 2025 8:37pm

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 represents a significant product pivot and major feature expansion, transforming the approval tool into "SendVelo" - a comprehensive ChatGPT-native approval workflow platform. The changes include substantial infrastructure updates, multi-reviewer support, organization management, and enhanced integrations.

Key Changes:

  • Complete product rebranding from generic approval tool to SendVelo with detailed implementation and strategy documentation
  • Multi-reviewer workflow system with parallel, sequential, and any-one approval modes
  • Organization/team management using Better Auth plugin
  • Enhanced MCP server with 5 new tools for comprehensive ChatGPT integration
  • Slack integration with interactive approvals
  • Version control and content format support (Markdown, HTML, plain text)

Reviewed changes

Copilot reviewed 63 out of 92 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
docs/CRITICAL_REVIEW.md Removed critical review document (cleanup)
docs/IMPLEMENTATION_PLAN.md Added comprehensive SendVelo implementation roadmap with Better Auth integration
docs/CHATGPT_APP_STORE_STRATEGY.md Added ChatGPT App Store optimization strategy with golden prompts
baseUrl.ts Removed file (architecture cleanup)
approval-tool/src/server/api/routers/user.ts Updated subscription tier checks from string to enum
approval-tool/src/server/api/routers/review.ts Major expansion: multi-reviewer support, workflows, version control, activity logs
approval-tool/src/server/api/routers/organization.ts Added complete organization management router
approval-tool/src/server/api/root.ts Added organization router to API
approval-tool/src/lib/templates.ts Added review templates for common use cases
approval-tool/src/lib/slack.ts Added Slack integration with retry logic and interactive notifications
approval-tool/src/lib/email.ts Enhanced email templates with one-click approve/reject links
approval-tool/src/lib/auth.ts Added organization plugin to Better Auth configuration
approval-tool/src/lib/auth-client.ts Added organization client plugin
approval-tool/src/env.ts Added Slack and additional Stripe environment variables
approval-tool/src/components/ui/toast.tsx Added toast notification system
approval-tool/src/components/content-renderer.tsx Added content renderer with XSS protection
approval-tool/prisma/schema.prisma Major schema expansion: organizations, teams, reviewers, versions, activity logs
approval-tool/package.json Updated dependencies for Slack, DOMPurify, diff
approval-tool/messages/en.json Added translations for new features
approval-tool/app/mcp/route.ts Major MCP server expansion with 5 new tools
approval-tool/app/layout.tsx Simplified layout (removed i18n/tRPC from root)
approval-tool/app/globals.css Added mobile-friendly styles and animations
approval-tool/app/api/webhooks/stripe/route.ts Updated subscription tier enum values
approval-tool/app/api/slack/interactions/route.ts Added Slack webhook handler with security validation
approval-tool/app/[locale]/layout.tsx Added locale-specific layout with providers
approval-tool/app/[locale]/review/[slug]/page.tsx Complete rewrite with mobile-first design and multi-reviewer UI
approval-tool/app/[locale]/approve/[reviewId]/[reviewerId]/page.tsx Added one-click email approval page
approval-tool/app/[locale]/(app)/dashboard/page.tsx Enhanced dashboard with search, filters, templates
Multiple legacy files Removed old route structures and pages
Files not reviewed (5)
  • .idea/modules.xml: Language not supported
  • .idea/sendvelo.iml: Language not supported
  • .idea/vcs.xml: Language not supported
  • .idea/workspace.xml: Language not supported
  • approval-tool/pnpm-lock.yaml: Language not supported

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


// Exponential backoff: 1s, 2s, 4s
const delay = baseDelayMs * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Missing 'void' operator before Promise in setTimeout. In TypeScript, floating promises should be explicitly marked. Add void before new Promise to indicate intentional fire-and-forget: await new Promise<void>(resolve => setTimeout(resolve, delay));

Suggested change
await new Promise(resolve => setTimeout(resolve, delay));
await new Promise<void>(resolve => setTimeout(resolve, delay));

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +89
const approveUrl = canBuildOneClickLinks
? `${baseUrl}/en/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=approve`
: reviewUrl;
const rejectUrl = canBuildOneClickLinks
? `${baseUrl}/en/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=reject`
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Hardcoded locale 'en' in URL construction. This should use a locale parameter or constant to support internationalization properly, as the app appears to support multiple locales based on the [locale] route structure.

Suggested change
const approveUrl = canBuildOneClickLinks
? `${baseUrl}/en/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=approve`
: reviewUrl;
const rejectUrl = canBuildOneClickLinks
? `${baseUrl}/en/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=reject`
// Derive locale from the reviewUrl path (e.g., /en/... or /de/...)
let locale = "en";
try {
const parsedReviewUrl = new URL(reviewUrl);
const pathSegments = parsedReviewUrl.pathname.split("/").filter(Boolean);
if (pathSegments.length > 0 && pathSegments[0]) {
locale = pathSegments[0];
}
} catch {
// If parsing fails, fall back to the default locale ("en")
}
const approveUrl = canBuildOneClickLinks
? `${baseUrl}/${locale}/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=approve`
: reviewUrl;
const rejectUrl = canBuildOneClickLinks
? `${baseUrl}/${locale}/approve/${reviewId}/${reviewerId}?token=${encodeURIComponent(token)}&decision=reject`

Copilot uses AI. Check for mistakes.
if (env.SLACK_SIGNING_SECRET) {
const crypto = await import("crypto");
const sigBasestring = `v0:${timestamp}:${rawBody}`;
const mySignature = 'v0=' + crypto.createHmac('sha256', env.SLACK_SIGNING_SECRET).update(sigBasestring, 'utf8').digest('hex');
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Use template literals for string concatenation for consistency: const mySignature = \v0=${crypto.createHmac('sha256', env.SLACK_SIGNING_SECRET).update(sigBasestring, 'utf8').digest('hex')}`;`

Suggested change
const mySignature = 'v0=' + crypto.createHmac('sha256', env.SLACK_SIGNING_SECRET).update(sigBasestring, 'utf8').digest('hex');
const mySignature = `v0=${crypto.createHmac('sha256', env.SLACK_SIGNING_SECRET).update(sigBasestring, 'utf8').digest('hex')}`;

Copilot uses AI. Check for mistakes.
// Sanitize HTML content with DOMPurify to prevent XSS attacks
const sanitizedHTML = DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'ul', 'ol', 'li', 'a', 'code', 'pre', 'blockquote', 'hr', 'img', 'div', 'span'],
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'id'],
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Allowing 'id' and 'class' attributes in sanitized HTML could enable CSS-based attacks or DOM clobbering. Consider removing these attributes from ALLOWED_ATTR unless specifically required for functionality.

Suggested change
ALLOWED_ATTR: ['href', 'src', 'alt', 'title', 'class', 'id'],
ALLOWED_ATTR: ['href', 'src', 'alt', 'title'],

Copilot uses AI. Check for mistakes.
try {
switch (name) {
case "send_for_review": {
const input = SendForReviewSchema.parse(args);
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Schema validation errors from Zod will be caught and handled generically at line 718-729, but users would benefit from more specific error messages about which field failed validation (e.g., 'Invalid email format for reviewer at index 2'). Consider enhancing the error handling to provide field-specific context.

Copilot uses AI. Check for mistakes.
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
onClick={() => {
if (confirm(t("reviews.deleteConfirm"))) {
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Using native confirm() dialog is not accessible and doesn't match the modern UI design. Consider implementing a custom confirmation modal component that matches the app's design system and provides better accessibility support (keyboard navigation, screen reader support).

Copilot uses AI. Check for mistakes.
</span>
<button
onClick={() => {
if (confirm(t("reviews.deleteConfirm"))) {
Copy link

Copilot AI Dec 22, 2025

Choose a reason for hiding this comment

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

Using native confirm() dialog is not accessible and doesn't match the modern UI design. Consider implementing a custom confirmation modal component that matches the app's design system and provides better accessibility support (keyboard navigation, screen reader support).

Copilot uses AI. Check for mistakes.
@filippofilip95 filippofilip95 marked this pull request as draft December 22, 2025 20:36
@filippofilip95 filippofilip95 marked this pull request as ready for review December 22, 2025 20:36
@claude
Copy link

claude bot commented Dec 22, 2025

Code Review: Feature/Brand Naming Exploration PR

Overall Assessment: This is a well-architected greenfield application with solid foundations, but contains several security vulnerabilities that need addressing before production deployment.


Critical Security Issues (Must Fix Before Merge)

1. XSS Vulnerability in MCP Route - approval-tool/app/mcp/route.ts:167-169

Only trims/limits length - no HTML sanitization. When contentFormat is HTML, this allows XSS attacks.

Fix: The ContentRenderer component already has proper DOMPurify sanitization - use the same approach in the MCP route before storing.

2. CSRF Vulnerability in One-Click Email Actions - approval-tool/app/[locale]/approve/[reviewId]/[reviewerId]/page.tsx:76-82

Clicking email link auto-submits decision with no confirmation - vulnerable to CSRF attacks via email client prefetching or malicious forwards.

Fix: Require user to click a confirmation button instead of auto-submitting on page load.

3. Missing Slack Signature Verification Enforcement - approval-tool/app/api/slack/interactions/route.ts:66

Logs warning but continues processing - allows unauthenticated requests.

Fix: Return 401 when SLACK_SIGNING_SECRET is missing.

4. XSS in Email Templates - approval-tool/src/lib/email.ts:99

User-provided title inserted directly into HTML without escaping.

Fix: HTML-escape user content or use a templating library.


High Priority Issues

5. Insecure Environment Variable Handling - approval-tool/src/env.ts:14-40

All security-critical variables are .optional() - app builds successfully without proper config, then fails at runtime.

Fix: Make required variables required at build time.

6. Access Token Security - approval-tool/prisma/schema.prisma:276-277

  • Tokens exposed in email URLs
  • No expiration enforcement at database level (accessExpires is optional)
  • Tokens visible in browser history, email HTML, server logs

Fix: Make accessExpires required with short lifetime (1-24 hours), enforce expiration in all queries.


Good Security Practices

✅ Slack Integration - Excellent signature verification with timing-safe comparison
✅ Content Rendering - Proper DOMPurify sanitization with allowlists
✅ Input Validation - Comprehensive Zod schemas across all tRPC routes
✅ Database Transactions - Atomic review creation prevents race conditions
✅ Protected Procedures - Proper authentication middleware for tRPC


Additional Observations

Performance: Good use of database indexes, pagination implemented in tRPC

Code Quality: Follows CLAUDE.md conventions well, good TypeScript type safety

Breaking Changes: This is a greenfield app in approval-tool/ subdirectory


Recommended Action Plan

Before Merging:

  1. Fix Issues SendVelo v1.0 - Complete Implementation + BMAD Documentation #1-4 (XSS, CSRF, Slack auth, email escaping)
  2. Fix Issue feat: improve Claude Code Review workflow instructions #5 (env validation)
  3. Add short-lived token expiration (#6)

Follow-up PR:
4. Add rate limiting to auth and MCP endpoints
5. Implement comprehensive error logging
6. Add email daily send limits
7. Complete Stripe webhook to handle all 4 subscription tiers


Summary

This is a professional, well-structured application following modern Next.js 15, tRPC, and Prisma patterns. The Slack integration and content rendering show good security awareness. However, the XSS vulnerabilities and CSRF issues are blockers for production deployment.

Recommendation: Request fixes for issues #1-6, then approve for merge.

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.

1 participant