Token导航 LogoToken导航TokenDH.com
研究检索敏感数据github未标认证来源可访问许可证需确认审计提醒

secure-error-handling安全错误处理

Agent Skill

secure-error-handling 用于记录任务执行中的错误、用户纠正、经验和能力缺口,适合在 Codex、Claude、Cursor、Gemini CLI 中希望让 Agent 持续沉淀问题、修正和最佳实践时使用。可结合来源仓库、安装命令和原始 README 继续核验具体用法。安装前建议确认权限范围、维护状态,以及是否会触发联网、命令执行或文件读写。

总安装

3,255

周安装

137

GitHub Stars

7

下载量

1,140
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:secure-error-handling(安全错误处理)
来源仓库:https://github.com/harperaa/secure-claude-skills
仓库路径:skills/secure-error-handling
安装命令:
npx skills add https://github.com/harperaa/secure-claude-skills --skill secure-error-handling
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/harperaa/secure-claude-skills --skill secure-error-handling

简介

用于记录任务执行中的错误与修正过程,形成经验知识库。

  • 适合希望 Agent 持续学习并改进自身行为的长期协作场景。
  • 通过 GitHub 安装并使用 npx 命令开启错误追踪与反馈循环。
  • 日志仅存储于本地或指定路径,敏感信息需手动脱敏后再保存。
  • secure-error-handling 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

Secure Error Handling - Preventing Information Leakage

The Error Message Problem

Error messages are designed to help developers debug. But in production, detailed errors help attackers more than they help users.

What Attackers Learn from Error Messages

Database structure:

Error: column 'credit_cards.number' does not exist

→ Attacker now knows you have a credit_cards table

File paths:

Error at /var/www/app/lib/payment.js:47

→ Attacker learns your directory structure

Dependencies:

Stripe API error: Invalid API key format

→ Attacker knows you use Stripe

System info:

PostgreSQL 9.4 connection failed

→ Attacker learns your database version and can look up known vulnerabilities

Real-World Information Leakage

According to SANS Institute research, 74% of successful attacks start with reconnaissance phase where attackers gather information about the target system. Error messages are a primary source of this intelligence.

Equifax Breach (2017): Detailed error messages revealed they were using Apache Struts with a known vulnerability. Attackers exploited this revealed information.

Our Error Handling Architecture

Environment-Aware Error Responses

Development Mode:

{
  error: "Database connection failed",
  stack: "Error: connection timeout at db.connect (database.js:42:15)...",
  context: "user-profile-update",
  timestamp: "2025-10-15T10:30:00Z"
}

→ Developers get full details for debugging

Production Mode:

{
  error: "Internal server error",
  message: "An unexpected error occurred. Please try again later."
}

→ Users get safe, generic message

The Logging Strategy

All errors are logged server-side with full details (for investigation), but only generic messages are sent to clients in production. This gives us debugging capability without information leakage.

Implementation Files

  • lib/errorHandler.ts - 5 error handlers for different scenarios

Available Error Handlers

1. handleApiError(error, context)

Use for: Unexpected errors (HTTP 500)

import { handleApiError } from '@/lib/errorHandler';

async function handler(request: NextRequest) {
  try {
    // Risky operation
    await processPayment(data);
    return NextResponse.json({ success: true });

  } catch (error) {
    return handleApiError(error, 'payment-processing');
    // Production: "Internal server error"
    // Development: Full stack trace
  }
}

Returns:

  • Development: Full error with stack trace
  • Production: Generic "Internal server error" message
  • HTTP Status: 500

2. handleValidationError(message, details)

Use for: Input validation failures (HTTP 400)

import { handleValidationError } from '@/lib/errorHandler';

if (!isValidEmail(email)) {
  return handleValidationError(
    'Validation failed',
    { email: 'Invalid email format' }
  );
}

Returns:

{
  "error": "Validation failed",
  "details": {
    "email": "Invalid email format"
  }
}
  • HTTP Status: 400
  • Both dev and production: Returns detailed field errors (helps users fix input)

3. handleForbiddenError(message)

Use for: Authorization failures (HTTP 403)

import { handleForbiddenError } from '@/lib/errorHandler';

// Check if user owns this resource
if (resource.userId !== userId) {
  return handleForbiddenError('You do not have access to this resource');
}

Returns:

{
  "error": "Forbidden",
  "message": "You do not have access to this resource"
}
  • HTTP Status: 403
  • Both dev and production: Returns the provided message

4. handleUnauthorizedError(message)

Use for: Authentication failures (HTTP 401)

import { handleUnauthorizedError } from '@/lib/errorHandler';
import { auth } from '@clerk/nextjs/server';

const { userId } = await auth();
if (!userId) {
  return handleUnauthorizedError('Authentication required');
}

Returns:

{
  "error": "Unauthorized",
  "message": "Authentication required"
}
  • HTTP Status: 401
  • Both dev and production: Returns the provided message
  • Default message: "Authentication required" if no message provided

5. handleNotFoundError(resource)

Use for: Resource not found (HTTP 404)

import { handleNotFoundError } from '@/lib/errorHandler';

const post = await db.posts.findOne({ id: postId });
if (!post) {
  return handleNotFoundError('Post');
}

Returns:

{
  "error": "Not found",
  "message": "Post not found"
}
  • HTTP Status: 404
  • Both dev and production: Returns resource-specific message

Complete Error Handling Examples

Example 1: Protected API Route with Full Error Handling

// app/api/posts/[id]/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { validateRequest } from '@/lib/validateRequest';
import { idSchema } from '@/lib/validation';
import {
  handleApiError,
  handleUnauthorizedError,
  handleForbiddenError,
  handleNotFoundError,
  handleValidationError
} from '@/lib/errorHandler';

export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    // Authentication check
    const { userId } = await auth();
    if (!userId) {
      return handleUnauthorizedError('Please sign in to view posts');
    }

    // Validate ID parameter
    const validation = validateRequest(idSchema, params.id);
    if (!validation.success) {
      return handleValidationError('Invalid post ID', { id: 'Must be valid ID' });
    }

    const postId = validation.data;

    // Fetch post
    const post = await db.posts.findOne({ id: postId });

    // Handle not found
    if (!post) {
      return handleNotFoundError('Post');
    }

    // Check authorization
    if (post.userId !== userId && !post.isPublic) {
      return handleForbiddenError('You do not have access to this post');
    }

    return NextResponse.json({ post });

  } catch (error) {
    // Catch unexpected errors
    return handleApiError(error, 'get-post');
  }
}

export async function DELETE(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return handleUnauthorizedError();
    }

    const validation = validateRequest(idSchema, params.id);
    if (!validation.success) {
      return handleValidationError('Invalid post ID', validation.error);
    }

    const postId = validation.data;
    const post = await db.posts.findOne({ id: postId });

    if (!post) {
      return handleNotFoundError('Post');
    }

    // Only post owner can delete
    if (post.userId !== userId) {
      return handleForbiddenError('Only the post author can delete this post');
    }

    await db.posts.delete({ id: postId });

    return NextResponse.json({ success: true });

  } catch (error) {
    return handleApiError(error, 'delete-post');
  }
}

Example 2: Payment Processing with Detailed Error Handling

// app/api/process-payment/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { withRateLimit } from '@/lib/withRateLimit';
import { withCsrf } from '@/lib/withCsrf';
import { auth } from '@clerk/nextjs/server';
import { handleApiError, handleUnauthorizedError, handleValidationError } from '@/lib/errorHandler';
import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

async function paymentHandler(request: NextRequest) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return handleUnauthorizedError();
    }

    const body = await request.json();
    const { amount, paymentMethodId } = body;

    // Validate amount
    if (!amount || amount < 50) {
      return handleValidationError('Invalid amount', {
        amount: 'Amount must be at least $0.50'
      });
    }

    // Process payment
    try {
      const paymentIntent = await stripe.paymentIntents.create({
        amount,
        currency: 'usd',
        payment_method: paymentMethodId,
        confirm: true,
        metadata: { userId }
      });

      return NextResponse.json({
        success: true,
        paymentIntentId: paymentIntent.id
      });

    } catch (stripeError: any) {
      // Handle Stripe-specific errors
      console.error('Stripe error:', stripeError);

      // Don't expose Stripe error details to client
      if (stripeError.type === 'StripeCardError') {
        return NextResponse.json(
          {
            error: 'Payment failed',
            message: 'Your card was declined. Please try a different payment method.'
          },
          { status: 400 }
        );
      }

      // Generic error for other Stripe issues
      return NextResponse.json(
        {
          error: 'Payment processing failed',
          message: 'Unable to process payment. Please try again later.'
        },
        { status: 500 }
      );
    }

  } catch (error) {
    // Catch-all for unexpected errors
    return handleApiError(error, 'process-payment');
  }
}

export const POST = withRateLimit(withCsrf(paymentHandler));

export const config = {
  runtime: 'nodejs',
};

Example 3: Database Operation with Error Handling

// app/api/users/[id]/profile/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { auth } from '@clerk/nextjs/server';
import { validateRequest } from '@/lib/validateRequest';
import { updateProfileSchema } from '@/lib/validation';
import {
  handleApiError,
  handleUnauthorizedError,
  handleForbiddenError,
  handleNotFoundError
} from '@/lib/errorHandler';

export async function PATCH(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  try {
    const { userId } = await auth();
    if (!userId) {
      return handleUnauthorizedError();
    }

    // Users can only update their own profile
    if (params.id !== userId) {
      return handleForbiddenError('You can only update your own profile');
    }

    const body = await request.json();

    // Validate input
    const validation = validateRequest(updateProfileSchema, body);
    if (!validation.success) {
      return validation.response;
    }

    const { displayName, bio, website } = validation.data;

    // Update profile
    try {
      const updatedProfile = await db.profiles.update(
        { userId },
        {
          displayName,
          bio,
          website,
          updatedAt: Date.now()
        }
      );

      if (!updatedProfile) {
        return handleNotFoundError('Profile');
      }

      return NextResponse.json({ profile: updatedProfile });

    } catch (dbError: any) {
      // Log database error for debugging
      console.error('Database error:', dbError);

      // Don't expose database structure to client
      if (dbError.code === 'UNIQUE_VIOLATION') {
        return NextResponse.json(
          {
            error: 'Update failed',
            message: 'This username is already taken'
          },
          { status: 409 }
        );
      }

      // Generic database error
      return NextResponse.json(
        {
          error: 'Database error',
          message: 'Failed to update profile. Please try again.'
        },
        { status: 500 }
      );
    }

  } catch (error) {
    return handleApiError(error, 'update-profile');
  }
}

Error Handler Implementation

lib/errorHandler.ts

import { NextResponse } from 'next/server';

export function handleApiError(error: unknown, context: string) {
  console.error(`[${context}] Error:`, error);

  if (process.env.NODE_ENV === 'production') {
    // Production: Generic error
    return NextResponse.json(
      {
        error: 'Internal server error',
        message: 'An unexpected error occurred. Please try again later.'
      },
      { status: 500 }
    );
  } else {
    // Development: Full error details
    return NextResponse.json(
      {
        error: 'Internal server error',
        message: error instanceof Error ? error.message : 'Unknown error',
        stack: error instanceof Error ? error.stack : undefined,
        context,
        timestamp: new Date().toISOString()
      },
      { status: 500 }
    );
  }
}

export function handleValidationError(
  message: string,
  details: Record<string, string>
) {
  return NextResponse.json(
    {
      error: 'Validation failed',
      message,
      details
    },
    { status: 400 }
  );
}

export function handleForbiddenError(message?: string) {
  return NextResponse.json(
    {
      error: 'Forbidden',
      message: message || 'Access denied'
    },
    { status: 403 }
  );
}

export function handleUnauthorizedError(message?: string) {
  return NextResponse.json(
    {
      error: 'Unauthorized',
      message: message || 'Authentication required'
    },
    { status: 401 }
  );
}

export function handleNotFoundError(resource: string) {
  return NextResponse.json(
    {
      error: 'Not found',
      message: `${resource} not found`
    },
    { status: 404 }
  );
}

Logging Best Practices

What to Log

✅ Safe to Log:

  • Error type/code
  • Context (which operation failed)
  • User ID (for tracking issues)
  • Timestamp
  • Request path
  • HTTP status code
  • IP addresses (for security monitoring)
  • Operation names
  • Last 4 digits of card (for reference only)
  • Transaction IDs

❌ Never Log:

  • Passwords (even hashed)
  • Credit card numbers (full)
  • CVV codes
  • API keys/secrets/tokens
  • Personal Identifiable Information (full addresses, SSN, etc.)
  • Session tokens
  • Encryption keys
  • Full request/response bodies (may contain sensitive data)
  • Environment variables (process.env)
  • Full error stack traces (in production)

Secure Logging Example

// ✅ Good logging
console.error('Payment failed', {
  userId,
  errorCode: error.code,
  errorType: error.type,
  timestamp: new Date().toISOString(),
  path: request.nextUrl.pathname
});

// ❌ Bad logging
console.error('Payment failed', {
  userId,
  creditCard: cardNumber, // ❌ Never log payment info
  apiKey: stripeKey,      // ❌ Never log secrets
  request: req.body       // ❌ May contain sensitive data
});

Redacting Sensitive Fields

Always redact sensitive data before logging:

const SENSITIVE_FIELDS = [
  'password', 'token', 'secret', 'apiKey', 'ssn',
  'creditCard', 'cvv', 'cardNumber'
];

function safelog(data: any) {
  const sanitized = { ...data };
  SENSITIVE_FIELDS.forEach(field => {
    if (field in sanitized) {
      sanitized[field] = '[REDACTED]';
    }
  });
  console.log(sanitized);
}

// Usage
safelog({
  userId: 'user123',
  email: 'user@example.com',
  password: 'secret123' // Will be [REDACTED]
});

Production Logging Setup

// lib/logger.ts
export function logSecurityEvent(event: {
  type: string;
  userId?: string;
  ip?: string;
  details?: Record<string, any>;
}) {
  const logEntry = {
    ...event,
    timestamp: new Date().toISOString(),
    environment: process.env.NODE_ENV
  };

  if (process.env.NODE_ENV === 'production') {
    // Send to logging service (Vercel logs, Datadog, etc.)
    console.log(JSON.stringify(logEntry));
  } else {
    // Pretty print in development
    console.log('Security Event:', logEntry);
  }
}

// Usage
logSecurityEvent({
  type: 'UNAUTHORIZED_ACCESS_ATTEMPT',
  userId,
  ip: request.ip,
  details: {
    path: request.nextUrl.pathname,
    method: request.method
  }
});

Client-Side Error Handling

Graceful Error Display

// components/ErrorDisplay.tsx
export function ErrorDisplay({ error }: { error: ApiError }) {
  const getMessage = () => {
    switch (error.status) {
      case 400:
        return error.details
          ? Object.entries(error.details).map(([field, msg]) =>
              `${field}: ${msg}`
            ).join(', ')
          : 'Invalid input. Please check your data.';
      case 401:
        return 'Please sign in to continue.';
      case 403:
        return 'You don\'t have permission to do that.';
      case 404:
        return 'The requested resource was not found.';
      case 429:
        return 'Too many requests. Please wait a moment.';
      case 500:
        return 'Something went wrong. Please try again later.';
      default:
        return 'An error occurred. Please try again.';
    }
  };

  return (
    <div className="error-message">
      {getMessage()}
    </div>
  );
}

Fetch with Error Handling

async function createPost(data: PostData) {
  try {
    const response = await fetch('/api/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

    if (!response.ok) {
      const error = await response.json();

      // Handle different error types
      switch (response.status) {
        case 400:
          // Validation error - show field errors
          if (error.details) {
            showFieldErrors(error.details);
          }
          break;
        case 401:
          // Redirect to login
          router.push('/sign-in');
          break;
        case 403:
          // Show access denied message
          alert(error.message);
          break;
        case 429:
          // Rate limited - show retry message
          alert(`Too many requests. Please wait ${error.retryAfter} seconds.`);
          break;
        default:
          // Generic error
          alert('An error occurred. Please try again.');
      }

      return null;
    }

    return await response.json();

  } catch (error) {
    console.error('Network error:', error);
    alert('Network error. Please check your connection.');
    return null;
  }
}

What Secure Error Handling Prevents

Information disclosure - No system details exposed ✅ System fingerprinting - Can't identify technology stack ✅ Database structure revelation - No schema details in errors ✅ Technology stack identification - Generic errors only ✅ Attack surface reconnaissance - Minimal information leakage ✅ Path disclosure - No file system paths exposed ✅ Version disclosure - No software versions revealed

Common Mistakes to Avoid

DON'T return error.message directly to clientsDON'T include stack traces in production responsesDON'T expose database errors to clientsDON'T log sensitive data (passwords, tokens, cards)DON'T use same error messages for dev and prodDON'T forget to log errors server-side for debugging

DO use handleApiError() for unexpected errorsDO use specific handlers for known error typesDO log errors server-side with contextDO return helpful (but safe) messages to usersDO use appropriate HTTP status codesDO sanitize error messages before sending to client

References

Next Steps

  • For input validation errors: Use input-validation skill with validateRequest()
  • For authentication errors: Use auth-security skill
  • For testing error responses: Use security-testing skill
  • For complete API security: Combine all error handlers appropriately

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

36.78%
按下载量换算419

Claude

28.79%
按下载量换算328

Cursor

16.28%
按下载量换算186

Gemini CLI

9.18%
按下载量换算105

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

可疑

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills